perf(feed): optimize posts rendering via props refactoring, memoizations

This commit is contained in:
Tom (plebeius.eth)
2025-03-03 23:47:33 +01:00
parent 4bdcfbdd28
commit 797a1f23c6
14 changed files with 206 additions and 112 deletions
+2 -1
View File
@@ -115,10 +115,11 @@ const CatalogPost = ({ post }: { post: Comment }) => {
subplebbitAddress,
timestamp,
title,
thumbnailUrl,
} = post || {};
const linkCount = useCountLinksInReplies(post);
const commentMediaInfo = useCommentMediaInfo(post);
const commentMediaInfo = useCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
const { hidden } = useHide({ cid });
+32 -13
View File
@@ -1,6 +1,5 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Comment } from '@plebbit/plebbit-react-hooks';
import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils';
import { getHostname } from '../../lib/utils/url-utils';
import useExpandedMediaStore from '../../stores/use-expanded-media-store';
@@ -14,12 +13,14 @@ interface MediaProps {
deleted?: boolean;
displayHeight?: string;
displayWidth?: string;
isDescription?: boolean;
isRules?: boolean;
isFloatingEmbed?: boolean;
isOutOfFeed?: boolean;
isReply?: boolean;
linkHeight?: number;
linkWidth?: number;
post?: Comment;
parentCid?: string;
removed?: boolean;
spoiler?: boolean;
showThumbnail?: boolean;
@@ -130,12 +131,12 @@ interface ImageProps {
displayHeight: string;
displayWidth: string;
isOutOfFeed: boolean;
post: Comment | undefined;
parentCid?: string;
spoiler?: boolean;
}
const Image = ({ commentMediaInfo, displayHeight, displayWidth, isOutOfFeed, post }: ImageProps) => {
const Image = ({ commentMediaInfo, displayHeight, displayWidth, isOutOfFeed, parentCid, spoiler }: ImageProps) => {
const { t } = useTranslation();
const { parentCid, spoiler } = post || {};
const { type, url } = commentMediaInfo || {};
const isReply = parentCid;
const isMobile = useIsMobile();
@@ -181,7 +182,7 @@ const Image = ({ commentMediaInfo, displayHeight, displayWidth, isOutOfFeed, pos
{mediaDimensions && `, ${mediaDimensions}`})
</div>
)}
{type && !isImageExpanded && <div className={styles.fileInfo}>{`${post?.spoiler ? `${t('spoiler')} - ` : ''} ${getDisplayMediaInfoType(type, t)}`}</div>}
{type && !isImageExpanded && <div className={styles.fileInfo}>{`${spoiler ? `${t('spoiler')} - ` : ''} ${getDisplayMediaInfoType(type, t)}`}</div>}
</span>
) : (
<span
@@ -197,8 +198,20 @@ const Image = ({ commentMediaInfo, displayHeight, displayWidth, isOutOfFeed, pos
);
};
const CommentMedia = ({ commentMediaInfo, isFloatingEmbed, post, showThumbnail, setShowThumbnail }: MediaProps) => {
const { deleted, linkHeight, linkWidth, parentCid, removed, spoiler } = post || {};
const CommentMedia = ({
commentMediaInfo,
deleted,
isDescription,
isFloatingEmbed,
isRules,
linkHeight,
linkWidth,
parentCid,
removed,
showThumbnail,
setShowThumbnail,
spoiler,
}: MediaProps) => {
const isReply = parentCid;
const { t } = useTranslation();
const isMobile = useIsMobile();
@@ -235,14 +248,20 @@ const CommentMedia = ({ commentMediaInfo, isFloatingEmbed, post, showThumbnail,
displayWidth = '100%';
displayHeight = '100%';
}
const { isDescription, isRules } = post || {}; // custom properties, not from api
const isOutOfFeed = isDescription || isRules || isFloatingEmbed || spoiler; // virtuoso wrapper unneeded
const isOutOfFeed = isDescription || isRules || isFloatingEmbed || spoiler || false; // virtuoso wrapper unneeded
return (
<span className={styles.content}>
{commentMediaInfo?.type === 'image' ? (
// images just enlarge when clicked, so they don't need two separate components
<Image commentMediaInfo={commentMediaInfo} displayHeight={displayHeight} displayWidth={displayWidth} isOutOfFeed={isOutOfFeed} post={post} />
<Image
commentMediaInfo={commentMediaInfo}
displayHeight={displayHeight}
displayWidth={displayWidth}
isOutOfFeed={isOutOfFeed}
parentCid={parentCid}
spoiler={spoiler}
/>
) : (
<>
<span className={`${showThumbnail ? styles.show : styles.hide} ${styles.thumbnail}`}>
@@ -259,9 +278,9 @@ const CommentMedia = ({ commentMediaInfo, isFloatingEmbed, post, showThumbnail,
setShowThumbnail={setShowThumbnail}
/>
)}
{isMobile && type && <div className={styles.fileInfo}>{`${post?.spoiler ? `${t('spoiler')} - ` : ''} ${getDisplayMediaInfoType(type, t)}`}</div>}
{isMobile && type && <div className={styles.fileInfo}>{`${spoiler ? `${t('spoiler')} - ` : ''} ${getDisplayMediaInfoType(type, t)}`}</div>}
</span>
{!showThumbnail && <Media commentMediaInfo={commentMediaInfo} isReply={post?.parentCid} setShowThumbnail={setShowThumbnail} />}
{!showThumbnail && <Media commentMediaInfo={commentMediaInfo} isReply={!!parentCid} setShowThumbnail={setShowThumbnail} />}
</>
)}
</span>
+59 -21
View File
@@ -4,7 +4,7 @@ import { Link, useLocation, useParams } from 'react-router-dom';
import { Comment, useAuthorAvatar, useEditedComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import styles from '../../views/post/post.module.css';
import { getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils';
import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils';
import { hashStringToColor, getTextColorForBackground } from '../../lib/utils/post-utils';
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { isValidURL } from '../../lib/utils/url-utils';
@@ -222,17 +222,21 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }:
);
};
const PostMedia = ({ post, hasThumbnail }: PostProps) => {
interface PostMediaProps {
commentMediaInfo: CommentMediaInfo | undefined;
hasThumbnail: boolean;
isDescription: boolean;
isRules: boolean;
spoiler: boolean;
deleted: boolean;
removed: boolean;
linkHeight: number;
linkWidth: number;
parentCid: string;
}
const PostMedia = ({ commentMediaInfo, hasThumbnail, isDescription, isRules, spoiler, deleted, removed, linkHeight, linkWidth, parentCid }: PostMediaProps) => {
const { t } = useTranslation();
const { spoiler, cid } = post || {};
// Reset state by remounting component when post changes
return <PostMediaContent key={cid} post={post} hasThumbnail={hasThumbnail} spoiler={spoiler} t={t} />;
};
const PostMediaContent = ({ post, hasThumbnail, spoiler, t }: { post: any; hasThumbnail: boolean | undefined; spoiler: boolean; t: any }) => {
const { isDescription, isRules } = post || {}; // custom properties, not from api
const commentMediaInfo = useCommentMediaInfo(post);
const { url } = commentMediaInfo || {};
let type = commentMediaInfo?.type;
const gifFrameUrl = useFetchGifFirstFrame(url);
@@ -247,7 +251,7 @@ const PostMediaContent = ({ post, hasThumbnail, spoiler, t }: { post: any; hasTh
const [showThumbnail, setShowThumbnail] = useState(true);
const mediaDimensions = getMediaDimensions(commentMediaInfo);
console.log('mediaDimensions', mediaDimensions);
return (
<div className={styles.file}>
<div className={styles.fileText}>
@@ -268,8 +272,7 @@ const PostMediaContent = ({ post, hasThumbnail, spoiler, t }: { post: any; hasTh
)}
{showThumbnail && !hasThumbnail && embedUrl && canEmbed(embedUrl) && (
<span>
{' '}
[
-[
<span className={styles.closeMedia} onClick={() => setShowThumbnail(false)}>
{t('open')}
</span>
@@ -281,10 +284,17 @@ const PostMediaContent = ({ post, hasThumbnail, spoiler, t }: { post: any; hasTh
<div className={styles.fileThumbnail}>
<CommentMedia
commentMediaInfo={commentMediaInfo}
post={post}
deleted={deleted}
isDescription={isDescription}
isRules={isRules}
removed={removed}
linkHeight={linkHeight}
linkWidth={linkWidth}
showThumbnail={showThumbnail}
setShowThumbnail={setShowThumbnail}
isOutOfFeed={isDescription || isRules}
parentCid={parentCid}
spoiler={spoiler}
/>
</div>
)}
@@ -300,11 +310,13 @@ const Reply = ({ openReplyModal, postReplyCount, reply, roles }: PostProps) => {
post = editedComment;
}
const { author, cid, deleted, link, postCid, reason, removed, subplebbitAddress } = post || {};
const { author, cid, deleted, link, linkHeight, linkWidth, postCid, reason, removed, spoiler, subplebbitAddress, thumbnailUrl, parentCid } = post || {};
const { isDescription, isRules } = post || {}; // custom properties, not from api
const isRouteLinkToReply = useLocation().pathname.startsWith(`/p/${subplebbitAddress}/c/${cid}`);
const { hidden } = useHide({ cid });
const commentMediaInfo = useCommentMediaInfo(post);
const commentMediaInfo = useCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
return (
@@ -312,7 +324,20 @@ const Reply = ({ openReplyModal, postReplyCount, reply, roles }: PostProps) => {
<div className={styles.sideArrows}>{'>>'}</div>
<div className={`${styles.reply} ${isRouteLinkToReply && styles.highlight}`} data-cid={cid} data-author-address={author?.shortAddress} data-post-cid={postCid}>
<PostInfo openReplyModal={openReplyModal} post={post} postReplyCount={postReplyCount} roles={roles} isHidden={hidden} />
{link && !hidden && !(deleted || removed) && isValidURL(link) && <PostMedia post={post} hasThumbnail={hasThumbnail} />}
{link && !hidden && !(deleted || removed) && isValidURL(link) && (
<PostMedia
commentMediaInfo={commentMediaInfo}
hasThumbnail={hasThumbnail}
isDescription={isDescription}
isRules={isRules}
spoiler={spoiler}
deleted={deleted}
removed={removed}
linkHeight={linkHeight}
linkWidth={linkWidth}
parentCid={parentCid}
/>
)}
{!hidden && (!(removed || deleted) || ((removed || deleted) && reason)) && <CommentContent comment={post} />}
</div>
</div>
@@ -321,7 +346,7 @@ const Reply = ({ openReplyModal, postReplyCount, reply, roles }: PostProps) => {
const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies = true }: PostProps) => {
const { t } = useTranslation();
const { author, cid, content, deleted, link, pinned, postCid, removed, state, subplebbitAddress } = post || {};
const { author, cid, content, deleted, link, linkHeight, linkWidth, pinned, postCid, removed, spoiler, state, subplebbitAddress, thumbnailUrl, parentCid } = post || {};
const { isDescription, isRules } = post || {}; // custom properties, not from api
const params = useParams();
const location = useLocation();
@@ -353,7 +378,7 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
replyCount: 0,
};
const commentMediaInfo = useCommentMediaInfo(post);
const commentMediaInfo = useCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
return (
@@ -373,7 +398,20 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
)}
<div data-cid={cid} data-author-address={author?.shortAddress} data-post-cid={postCid} className={shouldShowSnow() && hasThumbnail ? styles.xmasHatWrapper : ''}>
{shouldShowSnow() && hasThumbnail && <img src={`${process.env.PUBLIC_URL}/assets/xmashat.gif`} className={styles.xmasHat} alt='' />}
{link && !isHidden && !(deleted || removed) && isValidURL(link) && <PostMedia post={post} hasThumbnail={hasThumbnail} />}
{link && !isHidden && !(deleted || removed) && isValidURL(link) && (
<PostMedia
commentMediaInfo={commentMediaInfo}
hasThumbnail={hasThumbnail}
isDescription={isDescription}
isRules={isRules}
spoiler={spoiler}
deleted={deleted}
removed={removed}
linkHeight={linkHeight}
linkWidth={linkWidth}
parentCid={parentCid}
/>
)}
<PostInfo isHidden={hidden} openReplyModal={openReplyModal} post={post} postReplyCount={replyCount} roles={roles} />
{!isHidden && !content && !(deleted || removed) && <div className={styles.spacer} />}
{!isHidden && <CommentContent comment={post} />}
@@ -132,8 +132,8 @@ const BlockBoardButton = ({ address }: { address: string }) => {
const PostMenuDesktop = ({ post }: { post: Comment }) => {
const { t } = useTranslation();
const { author, cid, isDescription, isRules, link, postCid, subplebbitAddress } = post || {};
const commentMediaInfo = getCommentMediaInfo(post);
const { author, cid, isDescription, isRules, link, thumbnailUrl, linkWidth, linkHeight, postCid, subplebbitAddress } = post || {};
const commentMediaInfo = getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
const { thumbnail, type, url } = commentMediaInfo || {};
const [menuBtnRotated, setMenuBtnRotated] = useState(false);
@@ -122,9 +122,9 @@ const BlockBoardButton = ({ address }: { address: string }) => {
};
const PostMenuMobile = ({ post }: { post: Comment }) => {
const { author, cid, deleted, isDescription, isRules, link, parentCid, postCid, removed, subplebbitAddress } = post || {};
const { author, cid, deleted, isDescription, isRules, link, linkHeight, linkWidth, parentCid, postCid, removed, subplebbitAddress, thumbnailUrl } = post || {};
const { isAccountMod, isAccountCommentAuthor } = useEditCommentPrivileges({ commentAuthorAddress: author?.address, subplebbitAddress });
const commentMediaInfo = getCommentMediaInfo(post);
const commentMediaInfo = getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
const { thumbnail, type, url } = commentMediaInfo || {};
const [isMenuOpen, setIsMenuOpen] = useState(false);
const { refs, floatingStyles, context } = useFloating({
+32 -6
View File
@@ -27,7 +27,25 @@ import _ from 'lodash';
const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: PostProps) => {
const { t } = useTranslation();
const { author, cid, deleted, link, locked, parentCid, pinned, postCid, reason, removed, shortCid, state, subplebbitAddress, timestamp } = post || {};
const {
author,
cid,
deleted,
link,
linkHeight,
linkWidth,
locked,
parentCid,
pinned,
postCid,
reason,
removed,
shortCid,
state,
subplebbitAddress,
timestamp,
thumbnailUrl,
} = post || {};
const isReply = parentCid;
const title = post?.title?.trim();
const { isDescription, isRules } = post || {}; // custom properties, not from api
@@ -43,7 +61,7 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
const isInPostPageView = isPostPageView(location.pathname, params);
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
const commentMediaInfo = useCommentMediaInfo(post);
const commentMediaInfo = useCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
const stateString = useStateString(post);
@@ -179,25 +197,33 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
</span>
</span>
</div>
{(hasThumbnail || link) && !(deleted || removed) && <PostMediaContent key={cid} post={post} link={link} t={t} />}
{(hasThumbnail || link) && !(deleted || removed) && <PostMediaContent key={cid} post={post} link={link} />}
</>
);
};
const PostMediaContent = ({ post, link, t }: { post: any; link: string; t: any }) => {
const PostMediaContent = ({ post, link }: { post: any; link: string }) => {
const [showThumbnail, setShowThumbnail] = useState(true);
const { isDescription, isRules } = post || {}; // custom properties, not from api
const commentMediaInfo = useCommentMediaInfo(post);
const { thumbnailUrl, linkWidth, linkHeight, spoiler, deleted, removed, parentCid } = post || {};
const commentMediaInfo = useCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
return (
hasThumbnail && (
<CommentMedia
commentMediaInfo={commentMediaInfo}
post={post}
deleted={deleted}
isDescription={isDescription}
isRules={isRules}
removed={removed}
linkHeight={linkHeight}
linkWidth={linkWidth}
showThumbnail={showThumbnail}
setShowThumbnail={setShowThumbnail}
isOutOfFeed={isDescription || isRules}
parentCid={parentCid}
spoiler={spoiler}
/>
)
);
+2 -2
View File
@@ -33,8 +33,8 @@ const useCatalogFeedRows = (columnCount: number, feed: any, isFeedLoaded: boolea
// show account comments instantly in the feed once published (cid defined), instead of waiting for the feed to update
const filteredComments = accountComments.filter((comment) => {
const { cid, deleted, link, postCid, removed, state, subplebbitAddress, timestamp } = comment || {};
const commentMediaInfo = getCommentMediaInfo(comment);
const { cid, deleted, link, postCid, removed, state, subplebbitAddress, timestamp, thumbnailUrl, linkWidth, linkHeight } = comment || {};
const commentMediaInfo = getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
const isMediaShowed = getHasThumbnail(commentMediaInfo, link);
return (
+4 -5
View File
@@ -1,10 +1,9 @@
import { useCallback, useEffect } from 'react';
import { useLocation, useParams } from 'react-router-dom';
import { Comment } from '@plebbit/plebbit-react-hooks';
import { getCommentMediaInfo, fetchWebpageThumbnailIfNeeded } from '../lib/utils/media-utils';
import { isPendingPostView, isPostPageView } from '../lib/utils/view-utils';
export const useCommentMediaInfo = (comment: Comment) => {
export const useCommentMediaInfo = (link: string, thumbnailUrl: string, linkWidth: number, linkHeight: number) => {
const location = useLocation();
const params = useParams();
const isInPostPageView = isPostPageView(location.pathname, params);
@@ -12,7 +11,7 @@ export const useCommentMediaInfo = (comment: Comment) => {
// some sites have CORS access, so the thumbnail can be fetched client-side, which is helpful if subplebbit.settings.fetchThumbnailUrls is false
const fetchThumbnail = useCallback(async () => {
let commentMediaInfo = getCommentMediaInfo(comment);
let commentMediaInfo = getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
if (commentMediaInfo?.type === 'webpage' && !commentMediaInfo.thumbnail) {
const newMediaInfo = await fetchWebpageThumbnailIfNeeded(commentMediaInfo);
// Fetch the dimensions of the thumbnail
@@ -30,7 +29,7 @@ export const useCommentMediaInfo = (comment: Comment) => {
commentMediaInfo = newMediaInfo;
}
return commentMediaInfo;
}, [comment]);
}, [link, thumbnailUrl, linkWidth, linkHeight]);
useEffect(() => {
// don't fetch in feed view, it displaces the posts
@@ -39,5 +38,5 @@ export const useCommentMediaInfo = (comment: Comment) => {
}
}, [fetchThumbnail, isInPostPageView, isInPendingPostView]);
return getCommentMediaInfo(comment);
return getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
};
+2 -2
View File
@@ -22,10 +22,10 @@ const usePopularPosts = (subplebbits: Subplebbit[]) => {
if (subplebbit?.posts?.pages?.hot?.comments) {
for (const post of Object.values(subplebbit.posts.pages.hot.comments as Comment)) {
const { deleted, link, locked, pinned, removed, replyCount, timestamp } = post;
const { deleted, link, linkHeight, linkWidth, locked, pinned, removed, replyCount, thumbnailUrl, timestamp } = post;
try {
const commentMediaInfo = getCommentMediaInfo(post);
const commentMediaInfo = getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
if (
+56 -49
View File
@@ -1,5 +1,4 @@
import localForageLru from '@plebbit/plebbit-react-hooks/dist/lib/localforage-lru/index.js';
import { Comment } from '@plebbit/plebbit-react-hooks';
import extName from 'ext-name';
import { canEmbed } from '../../components/embed';
import memoize from 'memoizee';
@@ -13,7 +12,8 @@ export interface CommentMediaInfo {
thumbnailWidth?: number;
thumbnailHeight?: number;
patternThumbnailUrl?: string;
post?: Comment;
linkWidth?: number;
linkHeight?: number;
}
export const getDisplayMediaInfoType = (type: string, t: any) => {
@@ -35,19 +35,20 @@ export const getDisplayMediaInfoType = (type: string, t: any) => {
}
};
export const getHasThumbnail = (commentMediaInfo: CommentMediaInfo | undefined, link: string | undefined): boolean => {
const iframeThumbnail = commentMediaInfo?.patternThumbnailUrl || commentMediaInfo?.thumbnail;
return link &&
commentMediaInfo &&
(commentMediaInfo.type === 'image' ||
commentMediaInfo.type === 'video' ||
commentMediaInfo.type === 'audio' ||
commentMediaInfo.type === 'gif' ||
(commentMediaInfo.type === 'webpage' && commentMediaInfo.thumbnail) ||
(commentMediaInfo.type === 'iframe' && iframeThumbnail))
? true
: false;
};
export const getHasThumbnail = memoize(
(commentMediaInfo: CommentMediaInfo | undefined, link: string | undefined): boolean => {
if (!link || !commentMediaInfo) return false;
const { type, thumbnail, patternThumbnailUrl } = commentMediaInfo;
if (type === 'image' || type === 'video' || type === 'audio' || type === 'gif') return true;
if (type === 'webpage' && thumbnail) return true;
if (type === 'iframe' && (patternThumbnailUrl || thumbnail)) return true;
return false;
},
{ max: 1000 },
);
const getYouTubeVideoId = (url: URL): string | null => {
if (url.host.includes('youtu.be')) {
@@ -182,55 +183,61 @@ const fetchWebpageThumbnail = async (url: string): Promise<string | undefined> =
}
};
export const getCommentMediaInfo = (comment: Comment): CommentMediaInfo | undefined => {
if (!comment?.thumbnailUrl && !comment?.link) {
export const getCommentMediaInfo = (link: string, thumbnailUrl: string, linkWidth: number, linkHeight: number): CommentMediaInfo | undefined => {
if (!thumbnailUrl && !link) {
return;
}
const linkInfo = comment.link ? getLinkMediaInfo(comment.link) : undefined;
const linkInfo = link ? getLinkMediaInfo(link) : undefined;
if (linkInfo) {
return {
...linkInfo,
thumbnail: comment.thumbnailUrl || linkInfo.thumbnail,
post: comment,
thumbnail: thumbnailUrl || linkInfo.thumbnail,
linkWidth,
linkHeight,
};
}
return;
};
export const getMediaDimensions = (commentMediaInfo: CommentMediaInfo | undefined): string => {
if (!commentMediaInfo) return '';
const EMBED_DIMENSIONS = {
'youtube.com': '800x450',
'youtu.be': '800x450',
'instagram.com': '360x420',
'reddit.com': '500x520',
'tiktok.com': '400x780',
'x.com': '550x580',
'twitter.com': '550x580',
'soundcloud.com': '700x166',
} as const;
const { type, url, post } = commentMediaInfo;
export const getMediaDimensions = memoize(
(commentMediaInfo: CommentMediaInfo | undefined): string => {
if (!commentMediaInfo) return '';
if (type === 'iframe' && url) {
const embedUrl = new URL(url);
if (canEmbed(embedUrl)) {
// hardcoded dimensions from embed.module.css
if (embedUrl.hostname.includes('youtube.com') || embedUrl.hostname.includes('youtu.be')) {
return '800x450';
} else if (embedUrl.hostname.includes('instagram.com')) {
return '360x420';
} else if (embedUrl.hostname.includes('reddit.com')) {
return '500x520';
} else if (embedUrl.hostname.includes('tiktok.com')) {
return '400x780';
} else if (embedUrl.hostname.includes('x.com') || embedUrl.hostname.includes('twitter.com')) {
return '550x580';
} else if (embedUrl.hostname.includes('soundcloud.com')) {
return '700x166';
const { type, url, linkWidth, linkHeight } = commentMediaInfo;
if (type === 'iframe' && url) {
const embedUrl = new URL(url);
if (canEmbed(embedUrl)) {
const hostname = embedUrl.hostname;
for (const [site, dimensions] of Object.entries(EMBED_DIMENSIONS)) {
if (hostname.includes(site)) {
return dimensions;
}
}
}
} else if (type === 'audio') {
return '700x240';
} else if (type === 'image' || type === 'video' || type === 'gif') {
if (linkWidth && linkHeight) {
return `${linkWidth}x${linkHeight}`;
}
}
} else if (type === 'audio') {
return '700x240'; // hardcoded dimensions from embed.module.css
} else if (type === 'image' || type === 'video' || type === 'gif') {
// media dimensions calculated by API
if (post?.linkWidth && post?.linkHeight) {
return `${post.linkWidth}x${post.linkHeight}`;
}
}
return '';
};
return '';
},
{ max: 1000 },
);
const thumbnailUrlsDb = localForageLru.createInstance({ name: 'plebchanThumbnailUrls', size: 500 });
+3 -1
View File
@@ -53,7 +53,9 @@ const useCatalogFiltersStore = create(
// filterItems
} = state;
const hasThumbnail = getHasThumbnail(getCommentMediaInfo(comment), comment?.link);
const { link, linkHeight, linkWidth, thumbnailUrl } = comment || {};
const hasThumbnail = getHasThumbnail(getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight), link);
// const title = comment?.title?.toLowerCase() || '';
// const content = comment?.content?.toLowerCase() || '';
+4 -3
View File
@@ -24,7 +24,8 @@ import SubplebbitRules from '../../components/subplebbit-rules';
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
const threadsWithoutImagesFilter = (comment: Comment) => {
if (!getHasThumbnail(getCommentMediaInfo(comment), comment?.link)) {
const { link, linkHeight, linkWidth, thumbnailUrl } = comment || {};
if (!getHasThumbnail(getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight), link)) {
return false;
}
return true;
@@ -78,14 +79,14 @@ const Board = () => {
const filteredComments = useMemo(
() =>
accountComments.filter((comment) => {
const { cid, deleted, postCid, removed, state, timestamp } = comment || {};
const { cid, deleted, link, linkHeight, linkWidth, postCid, removed, state, thumbnailUrl, timestamp } = comment || {};
return (
!deleted &&
!removed &&
timestamp > Date.now() / 1000 - 60 * 60 &&
state === 'succeeded' &&
cid &&
(hideThreadsWithoutImages ? getHasThumbnail(getCommentMediaInfo(comment), comment?.link) : true) &&
(hideThreadsWithoutImages ? getHasThumbnail(getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight), comment?.link) : true) &&
cid === postCid &&
comment?.subplebbitAddress === subplebbitAddress &&
!feed.some((post) => post.cid === cid)
+4 -3
View File
@@ -22,7 +22,8 @@ import styles from './catalog.module.css';
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
const threadsWithoutImagesFilter = (comment: Comment) => {
if (!getHasThumbnail(getCommentMediaInfo(comment), comment?.link)) {
const { link, linkHeight, linkWidth, thumbnailUrl } = comment || {};
if (!getHasThumbnail(getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight), link)) {
return false;
}
return true;
@@ -98,14 +99,14 @@ const Catalog = () => {
const filteredComments = useMemo(
() =>
accountComments.filter((comment) => {
const { cid, deleted, postCid, removed, state, timestamp } = comment || {};
const { cid, deleted, link, linkHeight, linkWidth, postCid, removed, state, thumbnailUrl, timestamp } = comment || {};
return (
!deleted &&
!removed &&
timestamp > Date.now() / 1000 - 60 * 60 &&
state === 'succeeded' &&
cid &&
(hideThreadsWithoutImages ? getHasThumbnail(getCommentMediaInfo(comment), comment?.link) : true) &&
(hideThreadsWithoutImages ? getHasThumbnail(getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight), comment?.link) : true) &&
cid === postCid &&
comment?.subplebbitAddress === subplebbitAddress &&
!feed.some((post) => post.cid === cid)
@@ -26,8 +26,8 @@ export const ContentPreview = ({ content, maxLength = 99 }: { content: string; m
};
const PopularThreadCard = ({ post, boardTitle, boardShortAddress }: PopularThreadProps) => {
const { cid, content, subplebbitAddress, title } = post || {};
const commentMediaInfo = getCommentMediaInfo(post);
const { cid, content, link, linkHeight, linkWidth, subplebbitAddress, thumbnailUrl, title } = post || {};
const commentMediaInfo = getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
return (
<div className={styles.popularThread} key={cid}>