mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
@@ -10,8 +10,7 @@ const totalBanners = 57;
|
||||
const ImageBanner = () => {
|
||||
const [imagePath] = useState(() => {
|
||||
const randomBannerIndex = Math.floor(Math.random() * totalBanners) + 1;
|
||||
const cacheBuster = new Date().getTime();
|
||||
return `assets/banners/banner-${randomBannerIndex}.jpg?cacheBuster=${cacheBuster}`;
|
||||
return `/assets/banners/banner-${randomBannerIndex}.jpg`;
|
||||
});
|
||||
|
||||
return <img src={imagePath} alt='' />;
|
||||
@@ -28,20 +27,19 @@ const BoardBanner = () => {
|
||||
const subplebbitAddress = params?.subplebbitAddress || accountComment?.subplebbitAddress;
|
||||
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||
const displayAddress =
|
||||
subplebbit?.address && subplebbit?.address.length > 10 && !subplebbit?.address.includes('.') ? subplebbit?.address.slice(0, 13).concat('...') : subplebbit?.address;
|
||||
const { address, shortAddress } = subplebbit || {};
|
||||
|
||||
const multisubMetadata = useMultisubMetadata();
|
||||
|
||||
const title = isInAllView ? multisubMetadata?.title : isInSubscriptionsView ? 'Subscriptions' : subplebbit?.title;
|
||||
const subtitle = isInAllView ? 'p/all' : isInSubscriptionsView ? 'p/subscriptions' : `p/${subplebbit?.address}`;
|
||||
const subtitle = isInAllView ? 'p/all' : isInSubscriptionsView ? 'p/subscriptions' : `p/${address}`;
|
||||
|
||||
return (
|
||||
<div className={styles.content}>
|
||||
<div className={styles.bannerCnt}>
|
||||
<ImageBanner key={isInAllView ? 'all' : isInSubscriptionsView ? 'subscriptions' : subplebbit?.address} />
|
||||
<ImageBanner key={isInAllView ? 'all' : isInSubscriptionsView ? 'subscriptions' : address} />
|
||||
</div>
|
||||
<div className={styles.boardTitle}>{title || `p/${displayAddress}`}</div>
|
||||
<div className={styles.boardTitle}>{title || `p/${shortAddress}`}</div>
|
||||
<div className={styles.boardSubtitle}>{subtitle}</div>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
@@ -79,36 +79,20 @@
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.postMenuBtnPadding {
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
position: relative;
|
||||
.post .postMenu {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.postMenuBtn {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
line-height: 1em;
|
||||
color: var(--catalog-post-menu-btn-color);
|
||||
opacity: 0;
|
||||
transition: transform 0.1s;
|
||||
.post:hover .postMenu {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.post:hover .postMenuBtn {
|
||||
opacity: var(--catalog-post-menu-btn-opacity);
|
||||
.loadingSkeleton {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.post:hover .postMenuBtn:hover {
|
||||
cursor: pointer;
|
||||
color: var(--catalog-post-menu-btn-hover-color);
|
||||
opacity: var(--catalog-post-menu-btn-hover-opacity);
|
||||
}
|
||||
|
||||
.postMenuBtn {
|
||||
/* preserve unicode triangle for button, some browsers will use an emoji */
|
||||
font-variant: normal;
|
||||
font-feature-settings: "tnum";
|
||||
-webkit-font-feature-settings: "tnum"; /* Chrome, Opera, and Safari */
|
||||
-moz-font-feature-settings: "tnum"; /* Firefox */
|
||||
font-variant-numeric: tabular-nums;
|
||||
.fileDeleted {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
@@ -1,47 +1,31 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Comment } from '@plebbit/plebbit-react-hooks';
|
||||
import styles from './catalog-row.module.css';
|
||||
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
||||
import { isAllView } from '../../lib/utils/view-utils';
|
||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||
import styles from './catalog-row.module.css';
|
||||
import PostMenuDesktop from '../post/post-desktop/post-menu-desktop/';
|
||||
|
||||
export const CatalogPostMedia = ({ commentMediaInfo }: { commentMediaInfo: any }) => {
|
||||
import React, { useState } from 'react';
|
||||
|
||||
interface CatalogPostMediaProps {
|
||||
commentMediaInfo: any;
|
||||
isOutOfFeed?: boolean;
|
||||
linkWidth?: number;
|
||||
linkHeight?: number;
|
||||
}
|
||||
|
||||
export const CatalogPostMedia = ({ commentMediaInfo, isOutOfFeed, linkWidth, linkHeight }: CatalogPostMediaProps) => {
|
||||
const { patternThumbnailUrl, thumbnail, type, url } = commentMediaInfo || {};
|
||||
const iframeThumbnail = patternThumbnailUrl || thumbnail;
|
||||
const gifFrameUrl = useFetchGifFirstFrame(type === 'gif' ? url : undefined);
|
||||
let thumbnailComponent: React.ReactNode = null;
|
||||
|
||||
if (type === 'image') {
|
||||
thumbnailComponent = <img src={url} alt='' />;
|
||||
} else if (type === 'video') {
|
||||
thumbnailComponent = thumbnail ? <img src={thumbnail} alt='' /> : <video src={`${url}#t=0.001`} />;
|
||||
} else if (type === 'webpage') {
|
||||
thumbnailComponent = <img src={thumbnail} alt='' />;
|
||||
} else if (type === 'iframe') {
|
||||
thumbnailComponent = iframeThumbnail ? <img src={iframeThumbnail} alt='' /> : null;
|
||||
} else if (type === 'gif' && gifFrameUrl) {
|
||||
thumbnailComponent = <img src={gifFrameUrl} alt='' />;
|
||||
} else if (type === 'audio') {
|
||||
thumbnailComponent = <audio src={url} controls />;
|
||||
}
|
||||
return thumbnailComponent;
|
||||
};
|
||||
|
||||
interface CatalogPostProps {
|
||||
post: Comment;
|
||||
openMenu: boolean;
|
||||
toggleMenu: () => void;
|
||||
}
|
||||
|
||||
const CatalogPost = ({ openMenu, post, toggleMenu }: CatalogPostProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { cid, content, isDescription, isRules, link, linkHeight, linkWidth, locked, pinned, replyCount, subplebbitAddress, title } = post || {};
|
||||
const commentMediaInfo = getCommentMediaInfo(post);
|
||||
const { type } = commentMediaInfo || {};
|
||||
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
|
||||
const [isLoaded, setIsLoaded] = useState(false);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
const handleLoad = () => setIsLoaded(true);
|
||||
const handleError = () => setHasError(true);
|
||||
const loadingStyle = { display: isLoaded ? 'block' : 'none' };
|
||||
|
||||
let displayWidth, displayHeight;
|
||||
const maxThumbnailSize = 150;
|
||||
@@ -55,13 +39,47 @@ const CatalogPost = ({ openMenu, post, toggleMenu }: CatalogPostProps) => {
|
||||
displayHeight = `${maxThumbnailSize}px`;
|
||||
}
|
||||
|
||||
if (type === 'audio' || isDescription || isRules) {
|
||||
if (type === 'audio' || isOutOfFeed) {
|
||||
displayWidth = 'unset';
|
||||
displayHeight = 'unset';
|
||||
}
|
||||
|
||||
const thumbnailDimensions = { '--width': displayWidth, '--height': displayHeight } as React.CSSProperties;
|
||||
|
||||
let thumbnailComponent: React.ReactNode = null;
|
||||
|
||||
if (type === 'image' && !hasError) {
|
||||
thumbnailComponent = <img src={url} alt='' onLoad={handleLoad} onError={handleError} style={loadingStyle} />;
|
||||
} else if (type === 'video' && !hasError) {
|
||||
thumbnailComponent = thumbnail ? (
|
||||
<img src={thumbnail} alt='' onLoad={handleLoad} onError={handleError} style={loadingStyle} />
|
||||
) : (
|
||||
<video src={`${url}#t=0.001`} onLoad={handleLoad} onError={handleError} style={loadingStyle} />
|
||||
);
|
||||
} else if (type === 'webpage' && !hasError) {
|
||||
thumbnailComponent = <img src={thumbnail} alt='' onLoad={handleLoad} onError={handleError} style={loadingStyle} />;
|
||||
} else if (type === 'iframe' && iframeThumbnail && !hasError) {
|
||||
thumbnailComponent = <img src={iframeThumbnail} alt='' onLoad={handleLoad} onError={handleError} style={loadingStyle} />;
|
||||
} else if (type === 'gif' && gifFrameUrl && !hasError) {
|
||||
thumbnailComponent = <img src={gifFrameUrl} alt='' onLoad={handleLoad} onError={handleError} style={loadingStyle} />;
|
||||
} else if (type === 'audio') {
|
||||
thumbnailComponent = <audio src={url} controls />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={hasError ? '' : styles.mediaWrapper} style={thumbnailDimensions}>
|
||||
{!isLoaded && !hasError && <span className={styles.loadingSkeleton} />}
|
||||
{hasError ? <img className={styles.fileDeleted} src='/assets/filedeleted-res.gif' alt='File deleted' /> : thumbnailComponent}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CatalogPost = ({ post }: { post: Comment }) => {
|
||||
const { t } = useTranslation();
|
||||
const { cid, content, isDescription, isRules, link, linkHeight, linkWidth, locked, pinned, replyCount, subplebbitAddress, title } = post || {};
|
||||
const commentMediaInfo = getCommentMediaInfo(post);
|
||||
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
|
||||
|
||||
const location = useLocation();
|
||||
const isInAllView = isAllView(location.pathname);
|
||||
|
||||
@@ -76,28 +94,13 @@ const CatalogPost = ({ openMenu, post, toggleMenu }: CatalogPostProps) => {
|
||||
</div>
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handlePageClick = () => {
|
||||
if (openMenu) {
|
||||
toggleMenu();
|
||||
}
|
||||
};
|
||||
document.addEventListener('click', handlePageClick);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('click', handlePageClick);
|
||||
};
|
||||
}, [openMenu, toggleMenu]);
|
||||
|
||||
return (
|
||||
<div className={styles.post}>
|
||||
{hasThumbnail ? (
|
||||
<Link to={postLink}>
|
||||
<div className={styles.mediaPaddingWrapper}>
|
||||
{threadIcons}
|
||||
<div className={styles.mediaWrapper} style={thumbnailDimensions}>
|
||||
<CatalogPostMedia commentMediaInfo={commentMediaInfo} />
|
||||
</div>
|
||||
<CatalogPostMedia commentMediaInfo={commentMediaInfo} isOutOfFeed={isDescription || isRules} linkWidth={linkWidth} linkHeight={linkHeight} />
|
||||
</div>
|
||||
</Link>
|
||||
) : (
|
||||
@@ -111,18 +114,8 @@ const CatalogPost = ({ openMenu, post, toggleMenu }: CatalogPostProps) => {
|
||||
/ L: <b>{linkCount}</b>
|
||||
</span>
|
||||
)}
|
||||
<span className={styles.postMenuBtnPadding}>
|
||||
<span
|
||||
className={styles.postMenuBtn}
|
||||
title='Thread Menu'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
toggleMenu();
|
||||
}}
|
||||
style={{ transform: openMenu ? 'rotate(90deg)' : 'rotate(0deg)' }}
|
||||
>
|
||||
▶
|
||||
</span>
|
||||
<span className={styles.postMenu}>
|
||||
<PostMenuDesktop post={post} />
|
||||
</span>
|
||||
</div>
|
||||
<Link to={postLink}>
|
||||
@@ -141,16 +134,10 @@ interface CatalogRowProps {
|
||||
}
|
||||
|
||||
const CatalogRow = ({ row }: CatalogRowProps) => {
|
||||
const [postWithMenuOpen, setPostWithMenuOpen] = useState<number | null>(null);
|
||||
|
||||
const handleToggleMenu = (index: number) => {
|
||||
setPostWithMenuOpen(postWithMenuOpen === index ? null : index);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.row}>
|
||||
{row.map((post, index) => (
|
||||
<CatalogPost key={index} post={post} openMenu={postWithMenuOpen === index} toggleMenu={() => handleToggleMenu(index)} />
|
||||
<CatalogPost key={index} post={post} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Challenge as ChallengeType } from '@plebbit/plebbit-react-hooks';
|
||||
import { getPublicationType } from '../../lib/utils/challenge-utils';
|
||||
import useChallenges from '../../hooks/use-challenges';
|
||||
import useWindowWidth from '../../hooks/use-window-width';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import styles from './challenge-modal.module.css';
|
||||
import _ from 'lodash';
|
||||
|
||||
@@ -54,7 +54,7 @@ const Challenge = ({ challenge, closeModal }: ChallengeProps) => {
|
||||
|
||||
// react-draggable requires a ref to the modal node
|
||||
const nodeRef = useRef(null);
|
||||
const isMobile = useWindowWidth() < 640;
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const modalContent = (
|
||||
<div className={styles.container} ref={nodeRef}>
|
||||
|
||||
@@ -18,6 +18,15 @@
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.subplebbitAvatar {
|
||||
max-width: 125px;
|
||||
max-height: 125px;
|
||||
margin: 3px 10px 5px 5px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnailSmall {
|
||||
background-color: var(--media-thumbnail-background-color);
|
||||
width: var(--width);
|
||||
@@ -103,4 +112,8 @@
|
||||
|
||||
.mediaDesktopOp img, .mediaDesktopOp video, .mediaDesktopOp iframe, .mediaDesktopOp audio {
|
||||
padding: 3px 20px 5px 0;
|
||||
}
|
||||
|
||||
.fileDeleted {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './comment-media.module.css';
|
||||
import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '../../lib/utils/media-utils';
|
||||
import { getHostname } from '../../lib/utils/url-utils';
|
||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||
import useWindowWidth from '../../hooks/use-window-width';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import Embed, { canEmbed } from '../embed';
|
||||
|
||||
interface MediaProps {
|
||||
@@ -18,29 +18,13 @@ interface MediaProps {
|
||||
toggleExpanded?: () => void;
|
||||
}
|
||||
|
||||
interface ThumbnailProps {
|
||||
style: React.CSSProperties;
|
||||
children: React.ReactNode;
|
||||
thumbnailSmallPadding?: string;
|
||||
}
|
||||
|
||||
const ThumbnailBig = ({ style, children }: ThumbnailProps) => (
|
||||
<span className={styles.thumbnailBig} style={style}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
||||
const ThumbnailSmall = ({ style, children, thumbnailSmallPadding }: ThumbnailProps) => (
|
||||
<span className={`${styles.thumbnailSmall} ${thumbnailSmallPadding}`} style={style}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
||||
const Thumbnail = ({ commentMediaInfo, isOutOfFeed, isReply, linkHeight, linkWidth, setShowThumbnail }: MediaProps) => {
|
||||
const { patternThumbnailUrl, thumbnail, type, url } = commentMediaInfo || {};
|
||||
const [hasError, setHasError] = useState(false);
|
||||
const handleError = () => setHasError(true);
|
||||
|
||||
let displayWidth, displayHeight;
|
||||
const isMobile = useWindowWidth() < 640;
|
||||
const isMobile = useIsMobile();
|
||||
const maxThumbnailSize = isMobile || isReply ? 125 : 250;
|
||||
|
||||
if (linkWidth && linkHeight) {
|
||||
@@ -63,7 +47,7 @@ const Thumbnail = ({ commentMediaInfo, isOutOfFeed, isReply, linkHeight, linkWid
|
||||
const hasThumbnail = getHasThumbnail(commentMediaInfo, url);
|
||||
|
||||
if (type === 'image') {
|
||||
thumbnailComponent = <img src={url} alt='' onClick={() => setShowThumbnail(false)} />;
|
||||
thumbnailComponent = <img src={url} alt='' onError={handleError} onClick={() => setShowThumbnail(false)} />;
|
||||
} else if (type === 'video') {
|
||||
thumbnailComponent = thumbnail ? <img src={thumbnail} alt='' /> : <video src={`${url}#t=0.001`} onClick={() => setShowThumbnail(false)} />;
|
||||
} else if (type === 'webpage') {
|
||||
@@ -76,15 +60,21 @@ const Thumbnail = ({ commentMediaInfo, isOutOfFeed, isReply, linkHeight, linkWid
|
||||
thumbnailComponent = <audio src={url} controls />;
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
thumbnailComponent = <img className={styles.fileDeleted} src='/assets/filedeleted-res.gif' alt='File deleted' />;
|
||||
}
|
||||
|
||||
const thumbnailSmallPadding = isMobile ? styles.thumbnailMobile : styles.thumbnailReplyDesktop;
|
||||
const thumbnailDimensions = { '--width': displayWidth, '--height': displayHeight } as React.CSSProperties;
|
||||
|
||||
const linkWithoutThumbnail = url && new URL(url);
|
||||
|
||||
return isOutOfFeed ? (
|
||||
return hasError ? (
|
||||
<img className={styles.fileDeleted} src='/assets/filedeleted-res.gif' alt='File deleted' />
|
||||
) : isOutOfFeed ? (
|
||||
<span className={styles.subplebbitAvatar}>{thumbnailComponent}</span>
|
||||
) : isMobile || isReply ? (
|
||||
<ThumbnailSmall style={thumbnailDimensions} thumbnailSmallPadding={thumbnailSmallPadding}>
|
||||
<span className={`${styles.thumbnailSmall} ${thumbnailSmallPadding}`} style={thumbnailDimensions}>
|
||||
{thumbnailComponent}
|
||||
{isMobile &&
|
||||
!hasThumbnail &&
|
||||
@@ -96,16 +86,18 @@ const Thumbnail = ({ commentMediaInfo, isOutOfFeed, isReply, linkHeight, linkWid
|
||||
{getHostname(url) || (url.length > 30 ? url.slice(0, 30) + '...' : url)}
|
||||
</a>
|
||||
))}
|
||||
</ThumbnailSmall>
|
||||
</span>
|
||||
) : (
|
||||
<ThumbnailBig style={thumbnailDimensions}>{thumbnailComponent}</ThumbnailBig>
|
||||
<span className={styles.thumbnailBig} style={thumbnailDimensions}>
|
||||
{thumbnailComponent}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const Media = ({ commentMediaInfo, isReply, setShowThumbnail }: MediaProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { thumbnail, type, url } = commentMediaInfo || {};
|
||||
const isMobile = useWindowWidth() < 640;
|
||||
const isMobile = useIsMobile();
|
||||
const mediaClass = isMobile ? styles.mediaMobile : isReply ? styles.mediaDesktopReply : styles.mediaDesktopOp;
|
||||
|
||||
return (
|
||||
@@ -142,7 +134,7 @@ const Media = ({ commentMediaInfo, isReply, setShowThumbnail }: MediaProps) => {
|
||||
|
||||
const CommentMedia = ({ commentMediaInfo, isOutOfFeed, isReply, linkHeight, linkWidth, showThumbnail, setShowThumbnail }: MediaProps) => {
|
||||
const { t } = useTranslation();
|
||||
const isMobile = useWindowWidth() < 640;
|
||||
const isMobile = useIsMobile();
|
||||
const { type, url } = commentMediaInfo || {};
|
||||
|
||||
return (
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useLocation, useParams } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useAccount } from '@plebbit/plebbit-react-hooks';
|
||||
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
||||
import styles from '../post.module.css';
|
||||
import { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '../../../lib/utils/media-utils';
|
||||
import { getFormattedDate } from '../../../lib/utils/time-utils';
|
||||
import { isValidURL } from '../../../lib/utils/url-utils';
|
||||
@@ -15,9 +16,8 @@ import CommentMedia from '../../comment-media';
|
||||
import { canEmbed } from '../../embed';
|
||||
import LoadingEllipsis from '../../loading-ellipsis';
|
||||
import Markdown from '../../markdown';
|
||||
import PostMenuDesktop from './post-menu-desktop/';
|
||||
import { PostProps } from '../post';
|
||||
import styles from '../post.module.css';
|
||||
import PostMenu from '../post-menu';
|
||||
import _ from 'lodash';
|
||||
|
||||
const PostInfo = ({ openReplyModal, post, roles }: PostProps) => {
|
||||
@@ -82,12 +82,12 @@ const PostInfo = ({ openReplyModal, post, roles }: PostProps) => {
|
||||
)}
|
||||
{pinned && (
|
||||
<span className={`${styles.stickyIconWrapper} ${!locked && styles.addPaddingBeforeReply}`}>
|
||||
<img src='assets/icons/sticky.gif' alt='' className={styles.stickyIcon} title={t('sticky')} />
|
||||
<img src='/assets/icons/sticky.gif' alt='' className={styles.stickyIcon} title={t('sticky')} />
|
||||
</span>
|
||||
)}
|
||||
{locked && (
|
||||
<span className={`${styles.closedIconWrapper} ${styles.addPaddingBeforeReply} ${pinned && styles.addPaddingInBetween}`}>
|
||||
<img src='assets/icons/closed.gif' alt='' className={styles.closedIcon} title={t('closed')} />
|
||||
<img src='/assets/icons/closed.gif' alt='' className={styles.closedIcon} title={t('closed')} />
|
||||
</span>
|
||||
)}
|
||||
{!isInPostView && !isReply && (
|
||||
@@ -102,7 +102,7 @@ const PostInfo = ({ openReplyModal, post, roles }: PostProps) => {
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<PostMenu post={post} />
|
||||
<PostMenuDesktop post={post} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './post-menu-desktop';
|
||||
@@ -0,0 +1,116 @@
|
||||
.postMenu {
|
||||
position: absolute;
|
||||
background-color: var(--post-menu-desktop-background-color);
|
||||
border-bottom: var(--post-menu-desktop-border-bottom);
|
||||
border-right: var(--post-menu-desktop-border-right);
|
||||
outline: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.postMenuBtnWrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.postMenuBtn {
|
||||
color: var(--post-menu-desktop-button-color);
|
||||
transition: transform 0.1s;
|
||||
transform: rotate(90deg);
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
}
|
||||
|
||||
.postMenuBtn:hover {
|
||||
cursor: pointer;
|
||||
color: var(--post-menu-desktop-button-color-hover);
|
||||
}
|
||||
|
||||
.postMenuItem {
|
||||
cursor: pointer;
|
||||
padding: 2px 4px;
|
||||
border: 1px solid var(--post-menu-desktop-item-border-color);
|
||||
background-color: var(--post-menu-desktop-item-background-color);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.postMenuItem:hover {
|
||||
background-color: var(--post-menu-desktop-item-hover-background-color);
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdownMenu {
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: -1px;
|
||||
display: none;
|
||||
border-bottom: var(--post-menu-desktop-border-bottom);
|
||||
border-right: var(--post-menu-desktop-border-right);
|
||||
}
|
||||
|
||||
.dropdown a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.dropdown:hover .dropdownMenu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.postMenuBtnCatalogWrapper {
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.postMenuBtnCatalog {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
line-height: 1em;
|
||||
color: var(--catalog-post-menu-desktop-btn-color);
|
||||
opacity: var(--catalog-post-menu-desktop-btn-opacity);
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
|
||||
.postMenuBtnCatalog:hover {
|
||||
cursor: pointer;
|
||||
color: var(--catalog-post-menu-desktop-btn-hover-color);
|
||||
opacity: var(--catalog-post-menu-desktop-btn-hover-opacity);
|
||||
}
|
||||
|
||||
.postMenuBtnCatalog {
|
||||
/* preserve unicode triangle for button, some browsers will use an emoji */
|
||||
font-variant: normal;
|
||||
font-feature-settings: "tnum";
|
||||
-webkit-font-feature-settings: "tnum"; /* Chrome, Opera, and Safari */
|
||||
-moz-font-feature-settings: "tnum"; /* Firefox */
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.postMenuBtn {
|
||||
width: 1em;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.postMenuBtnWrapper {
|
||||
position: relative;
|
||||
padding-right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.postMenuBtn {
|
||||
right: -2px;
|
||||
opacity: var(--post-menu-desktop-btn-opacity);
|
||||
}
|
||||
|
||||
.stickyIconWrapper {
|
||||
padding-right: 18px;
|
||||
}
|
||||
}
|
||||
+44
-23
@@ -1,11 +1,14 @@
|
||||
import { useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react';
|
||||
import { Comment } from '@plebbit/plebbit-react-hooks';
|
||||
import styles from './post-menu.module.css';
|
||||
import { getCommentMediaInfo } from '../../../lib/utils/media-utils';
|
||||
import { copyShareLinkToClipboard, isValidURL } from '../../../lib/utils/url-utils';
|
||||
import styles from './post-menu-desktop.module.css';
|
||||
import { getCommentMediaInfo } from '../../../../lib/utils/media-utils';
|
||||
import { copyShareLinkToClipboard, isValidURL } from '../../../../lib/utils/url-utils';
|
||||
import { isCatalogView } from '../../../../lib/utils/view-utils';
|
||||
|
||||
interface PostMenuProps {
|
||||
interface PostMenuDesktopProps {
|
||||
cid: string;
|
||||
isDescription?: boolean;
|
||||
isRules?: boolean;
|
||||
@@ -13,7 +16,7 @@ interface PostMenuProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuProps) => {
|
||||
const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuDesktopProps) => {
|
||||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
@@ -28,16 +31,23 @@ const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuProps) => {
|
||||
|
||||
const ImageSearchButton = ({ url, onClose }: { url: string; onClose: () => void }) => {
|
||||
const [isImageSearchMenuOpen, setIsImageSearchMenuOpen] = useState(false);
|
||||
|
||||
const { refs, floatingStyles } = useFloating({
|
||||
placement: 'right-start',
|
||||
middleware: [flip(), shift({ padding: 10 })],
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${styles.postMenuItem} ${styles.dropdown}`}
|
||||
onMouseOver={() => setIsImageSearchMenuOpen(true)}
|
||||
onMouseLeave={() => setIsImageSearchMenuOpen(false)}
|
||||
ref={refs.setReference}
|
||||
onClick={onClose}
|
||||
>
|
||||
Image search »
|
||||
{isImageSearchMenuOpen && (
|
||||
<div className={styles.dropdownMenu}>
|
||||
<div ref={refs.setFloating} style={floatingStyles} className={styles.dropdownMenu}>
|
||||
<a href={`https://lens.google.com/uploadbyurl?url=${url}`} target='_blank' rel='noreferrer'>
|
||||
<div className={styles.postMenuItem}>Google</div>
|
||||
</a>
|
||||
@@ -53,19 +63,26 @@ const ImageSearchButton = ({ url, onClose }: { url: string; onClose: () => void
|
||||
);
|
||||
};
|
||||
|
||||
const ViewOnButton = ({ cid, isDescription, isRules, subplebbitAddress, onClose }: PostMenuProps) => {
|
||||
const ViewOnButton = ({ cid, isDescription, isRules, subplebbitAddress, onClose }: PostMenuDesktopProps) => {
|
||||
const [isClientRedirectMenuOpen, setIsClientRedirectMenuOpen] = useState(false);
|
||||
const viewOnOtherClientLink = `p/${subplebbitAddress}${isDescription || isRules ? '' : `/c/${cid}`}`;
|
||||
|
||||
const { refs, floatingStyles } = useFloating({
|
||||
placement: 'right-start',
|
||||
middleware: [flip(), shift({ padding: 10 })],
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${styles.postMenuItem} ${styles.dropdown}`}
|
||||
onMouseOver={() => setIsClientRedirectMenuOpen(true)}
|
||||
onMouseLeave={() => setIsClientRedirectMenuOpen(false)}
|
||||
ref={refs.setReference}
|
||||
onClick={onClose}
|
||||
>
|
||||
View on »
|
||||
{isClientRedirectMenuOpen && (
|
||||
<div className={styles.dropdownMenu}>
|
||||
<div ref={refs.setFloating} style={floatingStyles} className={styles.dropdownMenu}>
|
||||
<a href={`https://seedit.app/#/${viewOnOtherClientLink}`} target='_blank' rel='noreferrer'>
|
||||
<div className={styles.postMenuItem}>Seedit</div>
|
||||
</a>
|
||||
@@ -78,17 +95,19 @@ const ViewOnButton = ({ cid, isDescription, isRules, subplebbitAddress, onClose
|
||||
);
|
||||
};
|
||||
|
||||
const PostMenu = ({ post }: { post: Comment }) => {
|
||||
const { cid, isDescription, isRules, link, subplebbitAddress } = post;
|
||||
const PostMenuDesktop = ({ post }: { post: Comment }) => {
|
||||
const { cid, isDescription, isRules, link, postCid, subplebbitAddress } = post || {};
|
||||
const commentMediaInfo = getCommentMediaInfo(post);
|
||||
const { thumbnail, type, url } = commentMediaInfo || {};
|
||||
const [menuBtnRotated, setMenuBtnRotated] = useState(false);
|
||||
|
||||
const isInCatalogView = isCatalogView(useLocation().pathname);
|
||||
|
||||
const { refs, floatingStyles, context } = useFloating({
|
||||
placement: 'bottom-start',
|
||||
open: menuBtnRotated,
|
||||
onOpenChange: setMenuBtnRotated,
|
||||
middleware: [offset({ mainAxis: 10, crossAxis: 5 }), flip({ fallbackAxisSideDirection: 'end' }), shift({ padding: 10 })],
|
||||
middleware: [offset({ mainAxis: isInCatalogView ? -2 : 6, crossAxis: isInCatalogView ? -1 : 5 }), flip({ fallbackAxisSideDirection: 'end' }), shift({ padding: 10 })],
|
||||
whileElementsMounted: autoUpdate,
|
||||
});
|
||||
const click = useClick(context);
|
||||
@@ -101,9 +120,9 @@ const PostMenu = ({ post }: { post: Comment }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className={styles.postMenuBtnWrapper} ref={refs.setReference} {...getReferenceProps()}>
|
||||
<span className={isInCatalogView ? styles.postMenuBtnCatalogWrapper : styles.postMenuBtnWrapper} ref={refs.setReference} {...getReferenceProps()}>
|
||||
<span
|
||||
className={styles.postMenuBtn}
|
||||
className={isInCatalogView ? styles.postMenuBtnCatalog : styles.postMenuBtn}
|
||||
title='Post menu'
|
||||
onClick={() => setMenuBtnRotated((prev) => !prev)}
|
||||
style={{ transform: menuBtnRotated ? 'rotate(90deg)' : 'rotate(0deg)' }}
|
||||
@@ -111,17 +130,19 @@ const PostMenu = ({ post }: { post: Comment }) => {
|
||||
▶
|
||||
</span>
|
||||
</span>
|
||||
{menuBtnRotated && (
|
||||
<FloatingFocusManager context={context} modal={false}>
|
||||
<div className={styles.postMenu} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
|
||||
{cid && subplebbitAddress && <CopyLinkButton cid={cid} subplebbitAddress={subplebbitAddress} onClose={handleClose} />}
|
||||
{link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && <ImageSearchButton url={url} onClose={handleClose} />}
|
||||
<ViewOnButton cid={cid} isDescription={isDescription} isRules={isRules} subplebbitAddress={subplebbitAddress} onClose={handleClose} />
|
||||
</div>
|
||||
</FloatingFocusManager>
|
||||
)}
|
||||
{menuBtnRotated &&
|
||||
createPortal(
|
||||
<FloatingFocusManager context={context} modal={false}>
|
||||
<div className={styles.postMenu} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
|
||||
{cid && subplebbitAddress && <CopyLinkButton cid={cid} subplebbitAddress={subplebbitAddress} onClose={handleClose} />}
|
||||
{link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && <ImageSearchButton url={url} onClose={handleClose} />}
|
||||
<ViewOnButton cid={cid} isDescription={isDescription} isRules={isRules} subplebbitAddress={subplebbitAddress} onClose={handleClose} />
|
||||
</div>
|
||||
</FloatingFocusManager>,
|
||||
document.body,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PostMenu;
|
||||
export default PostMenuDesktop;
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './post-menu';
|
||||
@@ -1,86 +0,0 @@
|
||||
.postMenu {
|
||||
position: absolute;
|
||||
background-color: var(--post-menu-background-color);
|
||||
border-bottom: var(--post-menu-border-bottom);
|
||||
border-right: var(--post-menu-border-right);
|
||||
outline: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.postMenuBtnWrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.postMenuBtn {
|
||||
color: var(--post-menu-button-color);
|
||||
transition: transform 0.1s;
|
||||
transform: rotate(90deg);
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
}
|
||||
|
||||
.postMenuBtn:hover {
|
||||
cursor: pointer;
|
||||
color: var(--post-menu-button-color-hover);
|
||||
}
|
||||
|
||||
.postMenuItem {
|
||||
cursor: pointer;
|
||||
padding: 2px 4px;
|
||||
border: 1px solid var(--post-menu-item-border-color);
|
||||
background-color: var(--post-menu-item-background-color);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.postMenuItem:hover {
|
||||
background-color: var(--post-menu-item-hover-background-color);
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdownMenu {
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: -1px;
|
||||
display: none;
|
||||
border-bottom: var(--post-menu-border-bottom);
|
||||
border-right: var(--post-menu-border-right);
|
||||
}
|
||||
|
||||
.dropdown a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.dropdown:hover .dropdownMenu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.postMenuBtn {
|
||||
width: 1em;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.postMenuBtnWrapper {
|
||||
position: relative;
|
||||
padding-right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.postMenuBtn {
|
||||
right: -2px;
|
||||
opacity: var(--post-menu-btn-opacity);
|
||||
}
|
||||
|
||||
.stickyIconWrapper {
|
||||
padding-right: 18px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './post-menu-mobile';
|
||||
@@ -0,0 +1,42 @@
|
||||
.postMenuBtn {
|
||||
color: var(--post-mobile-menu-button-color);
|
||||
line-height: 1em;
|
||||
width: 1em;
|
||||
text-align: center;
|
||||
transition: transform 0.1s;
|
||||
transform: rotate(90deg);
|
||||
-webkit-transform: rotate(90deg);
|
||||
float: left;
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.postMenu {
|
||||
font-size: 16px;
|
||||
line-height: 2.5em;
|
||||
background-color: var(--post-menu-mobile-background-color);
|
||||
border: var(--post-menu-mobile-border);
|
||||
border-right-width: var(---post-menu-mobile-border-right-width);
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.postMenu a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.postMenuItem {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
padding: 2px 4px;
|
||||
vertical-align: middle;
|
||||
border-bottom: var(--post-menu-mobile-item-border-bottom);
|
||||
}
|
||||
|
||||
.postMenuItem:hover {
|
||||
background-color: var(--post-menu-mobile-item-hover-background-color);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import { useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react';
|
||||
import { Comment } from '@plebbit/plebbit-react-hooks';
|
||||
import styles from './post-menu-mobile.module.css';
|
||||
import { getCommentMediaInfo } from '../../../../lib/utils/media-utils';
|
||||
import { copyShareLinkToClipboard, isValidURL } from '../../../../lib/utils/url-utils';
|
||||
|
||||
interface PostMenuMobileProps {
|
||||
cid: string;
|
||||
isDescription?: boolean;
|
||||
isRules?: boolean;
|
||||
subplebbitAddress: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuMobileProps) => {
|
||||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
copyShareLinkToClipboard(subplebbitAddress, cid);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<div className={styles.postMenuItem}>Copy link</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ImageSearchButtons = ({ url, onClose }: { url: string; onClose: () => void }) => {
|
||||
return (
|
||||
<div onClick={onClose}>
|
||||
<a href={`https://lens.google.com/uploadbyurl?url=${url}`} target='_blank' rel='noreferrer'>
|
||||
<div className={styles.postMenuItem}>Search image on Google</div>
|
||||
</a>
|
||||
<a href={`https://www.yandex.com/images/search?img_url=${url}&rpt=imageview`} target='_blank' rel='noreferrer'>
|
||||
<div className={styles.postMenuItem}>Search image on Yandex</div>
|
||||
</a>
|
||||
<a href={`https://saucenao.com/search.php?url=${url}`} target='_blank' rel='noreferrer'>
|
||||
<div className={styles.postMenuItem}>Search image on SauceNAO</div>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ViewOnButtons = ({ cid, isDescription, isRules, subplebbitAddress, onClose }: PostMenuMobileProps) => {
|
||||
const viewOnOtherClientLink = `p/${subplebbitAddress}${isDescription || isRules ? '' : `/c/${cid}`}`;
|
||||
|
||||
return (
|
||||
<div onClick={onClose}>
|
||||
<a href={`https://seedit.app/#/${viewOnOtherClientLink}`} target='_blank' rel='noreferrer'>
|
||||
<div className={styles.postMenuItem}>View on Seedit</div>
|
||||
</a>
|
||||
<a href={`https://plebones.netlify.app/#/${viewOnOtherClientLink}`} target='_blank' rel='noreferrer'>
|
||||
<div className={styles.postMenuItem}>View on Plebones</div>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const PostMenuMobile = ({ post }: { post: Comment }) => {
|
||||
const { cid, isDescription, isRules, link, subplebbitAddress } = post || {};
|
||||
const commentMediaInfo = getCommentMediaInfo(post);
|
||||
const { thumbnail, type, url } = commentMediaInfo || {};
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
const { refs, floatingStyles, context } = useFloating({
|
||||
placement: 'bottom-start',
|
||||
open: isMenuOpen,
|
||||
onOpenChange: setIsMenuOpen,
|
||||
middleware: [offset({ mainAxis: 3, crossAxis: 8 }), flip(), shift({ padding: 10 })],
|
||||
whileElementsMounted: autoUpdate,
|
||||
});
|
||||
const click = useClick(context);
|
||||
const dismiss = useDismiss(context);
|
||||
const role = useRole(context);
|
||||
const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss, role]);
|
||||
const headingId = useId();
|
||||
|
||||
const handleClose = () => setIsMenuOpen(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className={styles.postMenuBtn} title='Post menu' onClick={() => setIsMenuOpen((prev) => !prev)} ref={refs.setReference} {...getReferenceProps()}>
|
||||
...
|
||||
</span>
|
||||
{isMenuOpen &&
|
||||
createPortal(
|
||||
<FloatingFocusManager context={context} modal={false}>
|
||||
<div className={styles.postMenu} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
|
||||
{cid && subplebbitAddress && <CopyLinkButton cid={cid} subplebbitAddress={subplebbitAddress} onClose={handleClose} />}
|
||||
{link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && <ImageSearchButtons url={url} onClose={handleClose} />}
|
||||
<ViewOnButtons cid={cid} isDescription={isDescription} isRules={isRules} subplebbitAddress={subplebbitAddress} onClose={handleClose} />
|
||||
</div>
|
||||
</FloatingFocusManager>,
|
||||
document.body,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PostMenuMobile;
|
||||
@@ -3,6 +3,7 @@ import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||
import { useAccount } from '@plebbit/plebbit-react-hooks';
|
||||
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
||||
import styles from '../post.module.css';
|
||||
import { getCommentMediaInfo, getHasThumbnail } from '../../../lib/utils/media-utils';
|
||||
import { getFormattedDate } from '../../../lib/utils/time-utils';
|
||||
import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../../lib/utils/view-utils';
|
||||
@@ -12,8 +13,8 @@ import useStateString from '../../../hooks/use-state-string';
|
||||
import CommentMedia from '../../comment-media';
|
||||
import LoadingEllipsis from '../../loading-ellipsis';
|
||||
import Markdown from '../../markdown';
|
||||
import PostMenuMobile from './post-menu-mobile';
|
||||
import { PostProps } from '../post';
|
||||
import styles from '../post.module.css';
|
||||
import _ from 'lodash';
|
||||
|
||||
const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
|
||||
@@ -45,9 +46,7 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.postInfo}>
|
||||
<span className={styles.postMenuBtn} title='Post menu'>
|
||||
...
|
||||
</span>
|
||||
<PostMenuMobile post={post} />
|
||||
<span className={styles.nameBlock}>
|
||||
<span className={`${styles.name} ${(isDescription || isRules || authorRole) && styles.capcodeMod}`}>
|
||||
{shortDisplayName || _.capitalize(t('anonymous'))}
|
||||
@@ -56,12 +55,12 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
|
||||
{!(isDescription || isRules) && <span className={styles.address}>(u/{shortAddress || accountShortAddress})</span>}
|
||||
{pinned && (
|
||||
<span className={styles.stickyIconWrapper}>
|
||||
<img src='assets/icons/sticky.gif' alt='' className={styles.stickyIcon} title={t('sticky')} />
|
||||
<img src='/assets/icons/sticky.gif' alt='' className={styles.stickyIcon} title={t('sticky')} />
|
||||
</span>
|
||||
)}
|
||||
{locked && (
|
||||
<span className={`${styles.closedIconWrapper} ${pinned && styles.addPaddingInBetween}`}>
|
||||
<img src='assets/icons/closed.gif' alt='' className={styles.closedIcon} title={t('closed')} />
|
||||
<img src='/assets/icons/closed.gif' alt='' className={styles.closedIcon} title={t('closed')} />
|
||||
</span>
|
||||
)}
|
||||
{title && (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Role, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
import useWindowWidth from '../../hooks/use-window-width';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import styles from './post.module.css';
|
||||
import PostDesktop from './post-desktop';
|
||||
import PostMobile from './post-mobile';
|
||||
@@ -15,7 +15,7 @@ export interface PostProps {
|
||||
|
||||
const Post = ({ post, showAllReplies = false, openReplyModal }: PostProps) => {
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress: post?.subplebbitAddress });
|
||||
const isMobile = useWindowWidth() < 640;
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<div className={styles.thread}>
|
||||
|
||||
@@ -6,7 +6,7 @@ import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
||||
import { setAccount, useAccount } from '@plebbit/plebbit-react-hooks';
|
||||
import { isValidURL } from '../../lib/utils/url-utils';
|
||||
import useReply from '../../hooks/use-reply';
|
||||
import useWindowWidth from '../../hooks/use-window-width';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import styles from './reply-modal.module.css';
|
||||
import _ from 'lodash';
|
||||
|
||||
@@ -51,7 +51,7 @@ const ReplyModal = ({ closeModal, parentCid, scrollY }: ReplyModalProps) => {
|
||||
}, [replyIndex, resetContent, closeModal]);
|
||||
|
||||
const nodeRef = useRef<HTMLDivElement>(null);
|
||||
const isMobile = useWindowWidth() < 640;
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
// on mobile, the position is absolute instead of fixed, so we need to calculate the top position
|
||||
useEffect(() => {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import useWindowWidth from './use-window-width';
|
||||
|
||||
const useIsMobile = () => {
|
||||
const windowWidth = useWindowWidth();
|
||||
return windowWidth < 640;
|
||||
};
|
||||
|
||||
export default useIsMobile;
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import useWindowWidth from './use-window-width';
|
||||
import useIsMobile from './use-is-mobile';
|
||||
|
||||
const useReplyModal = () => {
|
||||
const [showReplyModal, setShowReplyModal] = useState(false);
|
||||
const [activeCid, setActiveCid] = useState<string | null>(null);
|
||||
|
||||
// on mobile, the position is absolute instead of fixed, so we need to calculate the top position
|
||||
const isMobile = useWindowWidth() < 640;
|
||||
const isMobile = useIsMobile();
|
||||
const [scrollY, setScrollY] = useState<number>(0);
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
|
||||
@@ -2,6 +2,8 @@ import { useEffect, useMemo } from 'react';
|
||||
import { useAccount, useSubplebbits } from '@plebbit/plebbit-react-hooks';
|
||||
import { create } from 'zustand';
|
||||
|
||||
const pendingFetchCid: { [cid: string]: boolean } = {};
|
||||
|
||||
const useSubplebbitsStats = (options: any) => {
|
||||
const { subplebbitAddresses, accountName } = options || {};
|
||||
const account = useAccount({ accountName });
|
||||
@@ -15,13 +17,15 @@ const useSubplebbitsStats = (options: any) => {
|
||||
}
|
||||
|
||||
subplebbits.forEach((subplebbit) => {
|
||||
if (subplebbit && subplebbit.statsCid && !subplebbitsStats[subplebbit.address]) {
|
||||
if (subplebbit && subplebbit.statsCid && !subplebbitsStats[subplebbit.address] && !pendingFetchCid[subplebbit.statsCid]) {
|
||||
pendingFetchCid[subplebbit.statsCid] = true;
|
||||
account.plebbit
|
||||
.fetchCid(subplebbit.statsCid)
|
||||
.then((fetchedStats: any) => {
|
||||
setSubplebbitStats(subplebbit.address, JSON.parse(fetchedStats));
|
||||
})
|
||||
.catch((error: any) => {
|
||||
pendingFetchCid[subplebbit.statsCid] = false;
|
||||
console.error('Fetching subplebbit stats failed', { subplebbitAddress: subplebbit.address, error });
|
||||
});
|
||||
}
|
||||
|
||||
+126
-84
@@ -25,10 +25,10 @@
|
||||
--body-font-size: 13px;
|
||||
|
||||
/* catalog post */
|
||||
--catalog-post-menu-btn-color: navy;
|
||||
--catalog-post-menu-btn-hover-color: red;
|
||||
--catalog-post-menu-btn-opacity: 0.5;
|
||||
--catalog-post-menu-btn-hover-opacity: 1;
|
||||
--catalog-post-menu-desktop-btn-color: navy;
|
||||
--catalog-post-menu-desktop-btn-hover-color: red;
|
||||
--catalog-post-menu-desktop-btn-opacity: 0.5;
|
||||
--catalog-post-menu-desktop-btn-hover-opacity: 1;
|
||||
|
||||
/* challenge modal */
|
||||
--challenge-modal-background-color: #f0e0d6;
|
||||
@@ -97,9 +97,9 @@
|
||||
--post-subject-font-weight: 700;
|
||||
--post-name-text-color: #117743;
|
||||
--post-name-font-weight: 700;
|
||||
--post-menu-button-color: #000080;
|
||||
--post-menu-btn-opacity: 0.8;
|
||||
--post-menu-button-color-hover: red;
|
||||
--post-menu-desktop-button-color: #000080;
|
||||
--post-menu-desktop-btn-opacity: 0.8;
|
||||
--post-menu-desktop-button-color-hover: red;
|
||||
--post-greentext-color: #789922;
|
||||
--post-replies-expand-button-background-image: url("/public/assets/buttons/plus-red.png");
|
||||
|
||||
@@ -119,13 +119,20 @@
|
||||
--post-form-field-input-focus-border: 1px solid #ea8;
|
||||
--post-form-field-input-color: #000;
|
||||
|
||||
/* post menu */
|
||||
--post-menu-border-bottom: 2px solid #d9bfb7;
|
||||
--post-menu-border-right: 1px solid #d9bfb7;
|
||||
--post-menu-background-color: #f0e0d6;
|
||||
--post-menu-item-border-color: #d9bfb7;
|
||||
--post-menu-item-background-color: #f0e0d6;
|
||||
--post-menu-item-hover-background-color: #ffe;
|
||||
/* post menu desktop */
|
||||
--post-menu-desktop-border-bottom: 2px solid #d9bfb7;
|
||||
--post-menu-desktop-border-right: 1px solid #d9bfb7;
|
||||
--post-menu-desktop-background-color: #f0e0d6;
|
||||
--post-menu-desktop-item-border-color: #d9bfb7;
|
||||
--post-menu-desktop-item-background-color: #f0e0d6;
|
||||
--post-menu-desktop-item-hover-background-color: #ffe;
|
||||
|
||||
/* post menu mobile */
|
||||
--post-menu-mobile-background-color: #f0e0d6;
|
||||
--post-menu-mobile-border: 1px solid #d9bfb7;
|
||||
--post-menu-mobile-border-right-width: 2px;
|
||||
--post-menu-mobile-item-border-bottom: 1px solid #d9bfb7;
|
||||
--post-menu-mobile-item-hover-background-color: #ffe;
|
||||
|
||||
/* post mobile */
|
||||
--post-mobile-background-color: #f5e9e1;
|
||||
@@ -204,10 +211,10 @@
|
||||
--body-font-size: 13px;
|
||||
|
||||
/* catalog post */
|
||||
--catalog-post-menu-btn-color: navy;
|
||||
--catalog-post-menu-btn-hover-color: #d00;
|
||||
--catalog-post-menu-btn-opacity: 0.5;
|
||||
--catalog-post-menu-btn-hover-opacity: 1;
|
||||
--catalog-post-menu-desktop-btn-color: navy;
|
||||
--catalog-post-menu-desktop-btn-hover-color: #d00;
|
||||
--catalog-post-menu-desktop-btn-opacity: 0.5;
|
||||
--catalog-post-menu-desktop-btn-hover-opacity: 1;
|
||||
|
||||
/* challenge modal */
|
||||
--challenge-modal-background-color: #d6daf0;
|
||||
@@ -275,9 +282,9 @@
|
||||
--post-subject-font-weight: 700;
|
||||
--post-name-text-color: #117743;
|
||||
--post-name-font-weight: 700;
|
||||
--post-menu-button-color: #34345c;
|
||||
--post-menu-btn-opacity: 0.8;
|
||||
--post-menu-button-color-hover: #d00;
|
||||
--post-menu-desktop-button-color: #34345c;
|
||||
--post-menu-desktop-btn-opacity: 0.8;
|
||||
--post-menu-desktop-button-color-hover: #d00;
|
||||
--post-greentext-color: #789922;
|
||||
--post-replies-expand-button-background-image: url("/public/assets/buttons/plus-blue.png");
|
||||
|
||||
@@ -297,13 +304,20 @@
|
||||
--post-form-field-input-focus-border: 1px solid #98e;
|
||||
--post-form-field-input-color: #000;
|
||||
|
||||
/* post menu */
|
||||
--post-menu-border-bottom: 2px solid #b7c5d9;
|
||||
--post-menu-border-right: 1px solid #b7c5d9;
|
||||
--post-menu-background-color: #d6daf0;
|
||||
--post-menu-item-border-color: #b7c5d9;
|
||||
--post-menu-item-background-color: #d6daf0;
|
||||
--post-menu-item-hover-background-color: #eef2ff;
|
||||
/* post menu desktop */
|
||||
--post-menu-desktop-border-bottom: 2px solid #b7c5d9;
|
||||
--post-menu-desktop-border-right: 1px solid #b7c5d9;
|
||||
--post-menu-desktop-background-color: #d6daf0;
|
||||
--post-menu-desktop-item-border-color: #b7c5d9;
|
||||
--post-menu-desktop-item-background-color: #d6daf0;
|
||||
--post-menu-desktop-item-hover-background-color: #eef2ff;
|
||||
|
||||
/* post menu mobile */
|
||||
--post-menu-mobile-background-color: #d6daf0;
|
||||
--post-menu-mobile-border: 1px solid #b7c5d9;
|
||||
--post-menu-mobile-border-right-width: 2px;
|
||||
--post-menu-mobile-item-border-bottom: 1px solid #b7c5d9;
|
||||
--post-menu-mobile-item-hover-background-color: #eef2ff;
|
||||
|
||||
/* post mobile */
|
||||
--post-mobile-background-color: #d6daf0;
|
||||
@@ -377,10 +391,10 @@
|
||||
--board-address-font-size: 10pt;
|
||||
|
||||
/* catalog post */
|
||||
--catalog-post-menu-btn-color: navy;
|
||||
--catalog-post-menu-btn-hover-color: red;
|
||||
--catalog-post-menu-btn-opacity: 0.5;
|
||||
--catalog-post-menu-btn-hover-opacity: 1;
|
||||
--catalog-post-menu-desktop-btn-color: navy;
|
||||
--catalog-post-menu-desktop-btn-hover-color: red;
|
||||
--catalog-post-menu-desktop-btn-opacity: 0.5;
|
||||
--catalog-post-menu-desktop-btn-hover-opacity: 1;
|
||||
|
||||
/* challenge modal */
|
||||
--challenge-modal-background-color: #f0e0d6;
|
||||
@@ -429,9 +443,9 @@
|
||||
--post-subject-font-weight: 700;
|
||||
--post-name-text-color: #117743;
|
||||
--post-name-font-weight: 700;
|
||||
--post-menu-button-color: #000080;
|
||||
--post-menu-btn-opacity: 0.8;
|
||||
--post-menu-button-color-hover: red;
|
||||
--post-menu-desktop-button-color: #000080;
|
||||
--post-menu-desktop-btn-opacity: 0.8;
|
||||
--post-menu-desktop-button-color-hover: red;
|
||||
--post-greentext-color: #789922;
|
||||
--post-replies-expand-button-background-image: url("/public/assets/buttons/plus-red.png");
|
||||
|
||||
@@ -445,13 +459,20 @@
|
||||
--post-form-field-title-font-size: 16px;
|
||||
--post-form-field-input-color: #000;
|
||||
|
||||
/* post menu */
|
||||
--post-menu-border-bottom: 1px solid #d9bfb7;
|
||||
--post-menu-border-right: unset;
|
||||
--post-menu-background-color: #f0e0d6;
|
||||
--post-menu-item-border-color: #d9bfb7;
|
||||
--post-menu-item-background-color: #f0e0d6;
|
||||
--post-menu-item-hover-background-color: #ffe;
|
||||
/* post menu desktop */
|
||||
--post-menu-desktop-border-bottom: 1px solid #d9bfb7;
|
||||
--post-menu-desktop-border-right: unset;
|
||||
--post-menu-desktop-background-color: #f0e0d6;
|
||||
--post-menu-desktop-item-border-color: #d9bfb7;
|
||||
--post-menu-desktop-item-background-color: #f0e0d6;
|
||||
--post-menu-desktop-item-hover-background-color: #ffe;
|
||||
|
||||
/* post menu mobile */
|
||||
--post-menu-mobile-background-color: #f0e0d6;
|
||||
--post-menu-mobile-border: 1px solid #d9bfb7;
|
||||
--post-menu-mobile-border-right-width: 2px;
|
||||
--post-menu-mobile-item-border-bottom: 1px solid #d9bfb7;
|
||||
--post-menu-mobile-item-hover-background-color: #ffe;
|
||||
|
||||
/* post mobile */
|
||||
--post-mobile-background-color: #f5e9e1;
|
||||
@@ -524,10 +545,10 @@
|
||||
--body-font-size: 16px;
|
||||
|
||||
/* catalog post */
|
||||
--catalog-post-menu-btn-color: navy;
|
||||
--catalog-post-menu-btn-hover-color: red;
|
||||
--catalog-post-menu-btn-opacity: 0.5;
|
||||
--catalog-post-menu-btn-hover-opacity: 1;
|
||||
--catalog-post-menu-desktop-btn-color: navy;
|
||||
--catalog-post-menu-desktop-btn-hover-color: red;
|
||||
--catalog-post-menu-desktop-btn-opacity: 0.5;
|
||||
--catalog-post-menu-desktop-btn-hover-opacity: 1;
|
||||
|
||||
/* challenge modal */
|
||||
--challenge-modal-background-color: #d6daf0;
|
||||
@@ -586,9 +607,9 @@
|
||||
--post-subject-font-weight: 700;
|
||||
--post-name-text-color: #117743;
|
||||
--post-name-font-weight: 700;
|
||||
--post-menu-button-color: #34345c;
|
||||
--post-menu-btn-opacity: 0.8;
|
||||
--post-menu-button-color-hover: #d00;
|
||||
--post-menu-desktop-button-color: #34345c;
|
||||
--post-menu-desktop-btn-opacity: 0.8;
|
||||
--post-menu-desktop-button-color-hover: #d00;
|
||||
--post-greentext-color: #789922;
|
||||
--post-replies-expand-button-background-image: url("/public/assets/buttons/plus-blue.png");
|
||||
|
||||
@@ -602,13 +623,20 @@
|
||||
--post-form-field-title-font-size: 16px;
|
||||
--post-form-field-input-color: #000;
|
||||
|
||||
/* post menu */
|
||||
--post-menu-border-bottom: 1px solid #b7c5d9;
|
||||
--post-menu-border-right: unset;
|
||||
--post-menu-background-color: #d6daf0;
|
||||
--post-menu-item-border-color: #b7c5d9;
|
||||
--post-menu-item-background-color: #d6daf0;
|
||||
--post-menu-item-hover-background-color: #eef2ff;
|
||||
/* post menu desktop */
|
||||
--post-menu-desktop-border-bottom: 1px solid #b7c5d9;
|
||||
--post-menu-desktop-border-right: unset;
|
||||
--post-menu-desktop-background-color: #d6daf0;
|
||||
--post-menu-desktop-item-border-color: #b7c5d9;
|
||||
--post-menu-desktop-item-background-color: #d6daf0;
|
||||
--post-menu-desktop-item-hover-background-color: #eef2ff;
|
||||
|
||||
/* post menu mobile */
|
||||
--post-menu-mobile-background-color: #d6daf0;
|
||||
--post-menu-mobile-border: 1px solid #b7c5d9;
|
||||
--post-menu-mobile-border-right-width: 2px;
|
||||
--post-menu-mobile-item-border-bottom: 1px solid #b7c5d9;
|
||||
--post-menu-mobile-item-hover-background-color: #eef2ff;
|
||||
|
||||
/* post mobile */
|
||||
--post-mobile-background-color: #d6daf0;
|
||||
@@ -678,10 +706,10 @@
|
||||
--board-address-font-size: x-small;
|
||||
|
||||
/* catalog post */
|
||||
--catalog-post-menu-btn-color: #5f89ac;
|
||||
--catalog-post-menu-btn-hover-color: #5f89ac;
|
||||
--catalog-post-menu-btn-opacity: 0.5;
|
||||
--catalog-post-menu-btn-hover-opacity: 1;
|
||||
--catalog-post-menu-desktop-btn-color: #5f89ac;
|
||||
--catalog-post-menu-desktop-btn-hover-color: #5f89ac;
|
||||
--catalog-post-menu-desktop-btn-opacity: 0.5;
|
||||
--catalog-post-menu-desktop-btn-hover-opacity: 1;
|
||||
|
||||
/* challenge modal */
|
||||
--challenge-modal-background-color: #282a2e;
|
||||
@@ -744,9 +772,9 @@
|
||||
--post-subject-font-weight: 700;
|
||||
--post-name-text-color: #c5c8c6;
|
||||
--post-name-font-weight: 700;
|
||||
--post-menu-button-color: #5F89AC;
|
||||
--post-menu-btn-opacity: 0.8;
|
||||
--post-menu-button-color-hover: #81a2be;
|
||||
--post-menu-desktop-button-color: #5F89AC;
|
||||
--post-menu-desktop-btn-opacity: 0.8;
|
||||
--post-menu-desktop-button-color-hover: #81a2be;
|
||||
--post-greentext-color: #b5bd68;
|
||||
--post-replies-expand-button-background-image: url("/public/assets/buttons/plus-dark.png");
|
||||
|
||||
@@ -767,13 +795,20 @@
|
||||
--post-form-field-input-appearance: none;
|
||||
--post-form-field-input-outline: none;
|
||||
|
||||
/* post menu */
|
||||
--post-menu-border-bottom: 1px solid #000;
|
||||
--post-menu-border-right: unset;
|
||||
--post-menu-background-color: #282a2e;
|
||||
--post-menu-item-border-color: #000;
|
||||
--post-menu-item-background-color: #282a2e;
|
||||
--post-menu-item-hover-background-color: #1d1f21;
|
||||
/* post menu desktop */
|
||||
--post-menu-desktop-border-bottom: 1px solid #000;
|
||||
--post-menu-desktop-border-right: unset;
|
||||
--post-menu-desktop-background-color: #282a2e;
|
||||
--post-menu-desktop-item-border-color: #000;
|
||||
--post-menu-desktop-item-background-color: #282a2e;
|
||||
--post-menu-desktop-item-hover-background-color: #1d1f21;
|
||||
|
||||
/* post menu mobile */
|
||||
--post-menu-mobile-background-color: #282a2e;
|
||||
--post-menu-mobile-border: 1px solid #000;
|
||||
--post-menu-mobile-border-right-width: 2px;
|
||||
--post-menu-mobile-item-border-bottom: 1px solid #000;
|
||||
--post-menu-mobile-item-hover-background-color: #1d1f21;
|
||||
|
||||
/* post mobile */
|
||||
--post-mobile-background-color: #282A2E;
|
||||
@@ -858,10 +893,10 @@
|
||||
--board-address-font-size: x-small;
|
||||
|
||||
/* catalog post */
|
||||
--catalog-post-menu-btn-color: #f60;
|
||||
--catalog-post-menu-btn-hover-color: #f30;
|
||||
--catalog-post-menu-btn-opacity: 0.5;
|
||||
--catalog-post-menu-btn-hover-opacity: 1;
|
||||
--catalog-post-menu-desktop-btn-color: #f60;
|
||||
--catalog-post-menu-desktop-btn-hover-color: #f30;
|
||||
--catalog-post-menu-desktop-btn-opacity: 0.5;
|
||||
--catalog-post-menu-desktop-btn-hover-opacity: 1;
|
||||
|
||||
/* challenge modal */
|
||||
--challenge-modal-background-color: #ddd;
|
||||
@@ -910,9 +945,9 @@
|
||||
--post-subject-font-weight: 700;
|
||||
--post-name-text-color: #004a99;
|
||||
--post-name-font-weight: 700;
|
||||
--post-menu-button-color: #FF6600;
|
||||
--post-menu-btn-opacity: 0.8;
|
||||
--post-menu-button-color-hover: #FF3300;
|
||||
--post-menu-desktop-button-color: #FF6600;
|
||||
--post-menu-desktop-btn-opacity: 0.8;
|
||||
--post-menu-desktop-button-color-hover: #FF3300;
|
||||
--post-greentext-color: #789922;
|
||||
--post-replies-expand-button-background-image: url("/public/assets/buttons/plus-photon.png");
|
||||
|
||||
@@ -951,13 +986,20 @@
|
||||
--post-form-field-input-focus-border: 1px solid #ea8;
|
||||
--post-form-field-input-color: #000;
|
||||
|
||||
/* post menu */
|
||||
--post-menu-border-bottom: 1px solid #ccc;
|
||||
--post-menu-border-right: unset;
|
||||
--post-menu-background-color: #ddd;
|
||||
--post-menu-item-border-color: #ccc;
|
||||
--post-menu-item-background-color: #ddd;
|
||||
--post-menu-item-hover-background-color: #eee;
|
||||
/* post menu desktop */
|
||||
--post-menu-desktop-border-bottom: 1px solid #ccc;
|
||||
--post-menu-desktop-border-right: unset;
|
||||
--post-menu-desktop-background-color: #ddd;
|
||||
--post-menu-desktop-item-border-color: #ccc;
|
||||
--post-menu-desktop-item-background-color: #ddd;
|
||||
--post-menu-desktop-item-hover-background-color: #eee;
|
||||
|
||||
/* post menu mobile */
|
||||
--post-menu-mobile-background-color: #ddd;
|
||||
--post-menu-mobile-border: 1px solid #ccc;
|
||||
--post-menu-mobile-border-right-width: 2px;
|
||||
--post-menu-mobile-item-border-bottom: 1px solid #ccc;
|
||||
--post-menu-mobile-item-hover-background-color: #eee;
|
||||
|
||||
/* quotelink */
|
||||
--post-quotelink-text-color: #f60;
|
||||
|
||||
@@ -202,7 +202,7 @@ const PopularThreadCard = ({ post, boardTitle, boardShortAddress }: PopularThrea
|
||||
<div className={styles.title}>{boardTitle || boardShortAddress}</div>
|
||||
<div className={styles.mediaContainer}>
|
||||
<Link to={`/p/${subplebbitAddress}/c/${cid}`}>
|
||||
<CatalogPostMedia commentMediaInfo={commentMediaInfo} />
|
||||
<CatalogPostMedia commentMediaInfo={commentMediaInfo} isOutOfFeed={true} />
|
||||
</Link>
|
||||
</div>
|
||||
<div className={styles.threadContent}>
|
||||
|
||||
@@ -10,8 +10,7 @@ const totalNotFoundImages = 2;
|
||||
const NotFoundImage = () => {
|
||||
const [imagePath] = useState(() => {
|
||||
const randomBannerIndex = Math.floor(Math.random() * totalNotFoundImages) + 1;
|
||||
const cacheBuster = new Date().getTime();
|
||||
return `assets/not-found/not-found-${randomBannerIndex}.jpg?cacheBuster=${cacheBuster}`;
|
||||
return `/assets/not-found/not-found-${randomBannerIndex}.jpg`;
|
||||
});
|
||||
|
||||
return <img src={imagePath} alt='' />;
|
||||
|
||||
Reference in New Issue
Block a user