From 797a1f23c69374ce09b7a1ed7914e40e29acc4ca Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Mon, 3 Mar 2025 23:47:33 +0100 Subject: [PATCH] perf(feed): optimize posts rendering via props refactoring, memoizations --- src/components/catalog-row/catalog-row.tsx | 3 +- .../comment-media/comment-media.tsx | 45 +++++--- src/components/post-desktop/post-desktop.tsx | 80 +++++++++---- .../post-menu-desktop/post-menu-desktop.tsx | 4 +- .../post-menu-mobile/post-menu-mobile.tsx | 4 +- src/components/post-mobile/post-mobile.tsx | 38 ++++++- src/hooks/use-catalog-feed-rows.ts | 4 +- src/hooks/use-comment-media-info.ts | 9 +- src/hooks/use-popular-posts.ts | 4 +- src/lib/utils/media-utils.ts | 105 ++++++++++-------- src/stores/use-catalog-filters-store.ts | 4 +- src/views/board/board.tsx | 7 +- src/views/catalog/catalog.tsx | 7 +- .../popular-threads-box.tsx | 4 +- 14 files changed, 206 insertions(+), 112 deletions(-) diff --git a/src/components/catalog-row/catalog-row.tsx b/src/components/catalog-row/catalog-row.tsx index 87be9379..213e97e7 100644 --- a/src/components/catalog-row/catalog-row.tsx +++ b/src/components/catalog-row/catalog-row.tsx @@ -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 }); diff --git a/src/components/comment-media/comment-media.tsx b/src/components/comment-media/comment-media.tsx index 3b26aa40..05ddb3b8 100644 --- a/src/components/comment-media/comment-media.tsx +++ b/src/components/comment-media/comment-media.tsx @@ -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}`}) )} - {type && !isImageExpanded &&
{`${post?.spoiler ? `${t('spoiler')} - ` : ''} ${getDisplayMediaInfoType(type, t)}`}
} + {type && !isImageExpanded &&
{`${spoiler ? `${t('spoiler')} - ` : ''} ${getDisplayMediaInfoType(type, t)}`}
} ) : ( { - 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 ( {commentMediaInfo?.type === 'image' ? ( // images just enlarge when clicked, so they don't need two separate components - + ) : ( <> @@ -259,9 +278,9 @@ const CommentMedia = ({ commentMediaInfo, isFloatingEmbed, post, showThumbnail, setShowThumbnail={setShowThumbnail} /> )} - {isMobile && type &&
{`${post?.spoiler ? `${t('spoiler')} - ` : ''} ${getDisplayMediaInfoType(type, t)}`}
} + {isMobile && type &&
{`${spoiler ? `${t('spoiler')} - ` : ''} ${getDisplayMediaInfoType(type, t)}`}
}
- {!showThumbnail && } + {!showThumbnail && } )}
diff --git a/src/components/post-desktop/post-desktop.tsx b/src/components/post-desktop/post-desktop.tsx index 15c3e271..4feee886 100644 --- a/src/components/post-desktop/post-desktop.tsx +++ b/src/components/post-desktop/post-desktop.tsx @@ -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 ; -}; - -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 (
@@ -268,8 +272,7 @@ const PostMediaContent = ({ post, hasThumbnail, spoiler, t }: { post: any; hasTh )} {showThumbnail && !hasThumbnail && embedUrl && canEmbed(embedUrl) && ( - {' '} - [ + -[ setShowThumbnail(false)}> {t('open')} @@ -281,10 +284,17 @@ const PostMediaContent = ({ post, hasThumbnail, spoiler, t }: { post: any; hasTh
)} @@ -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) => {
{'>>'}
- {link && !hidden && !(deleted || removed) && isValidURL(link) && } + {link && !hidden && !(deleted || removed) && isValidURL(link) && ( + + )} {!hidden && (!(removed || deleted) || ((removed || deleted) && reason)) && }
@@ -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 )}
{shouldShowSnow() && hasThumbnail && } - {link && !isHidden && !(deleted || removed) && isValidURL(link) && } + {link && !isHidden && !(deleted || removed) && isValidURL(link) && ( + + )} {!isHidden && !content && !(deleted || removed) &&
} {!isHidden && } diff --git a/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx b/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx index 858983a9..fffcf83b 100644 --- a/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx +++ b/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx @@ -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); diff --git a/src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx b/src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx index 42361df5..3ef17fb5 100644 --- a/src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx +++ b/src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx @@ -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({ diff --git a/src/components/post-mobile/post-mobile.tsx b/src/components/post-mobile/post-mobile.tsx index d2837924..e031dc12 100644 --- a/src/components/post-mobile/post-mobile.tsx +++ b/src/components/post-mobile/post-mobile.tsx @@ -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
- {(hasThumbnail || link) && !(deleted || removed) && } + {(hasThumbnail || link) && !(deleted || removed) && } ); }; -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 && ( ) ); diff --git a/src/hooks/use-catalog-feed-rows.ts b/src/hooks/use-catalog-feed-rows.ts index dd3d1f81..bb995bbc 100644 --- a/src/hooks/use-catalog-feed-rows.ts +++ b/src/hooks/use-catalog-feed-rows.ts @@ -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 ( diff --git a/src/hooks/use-comment-media-info.ts b/src/hooks/use-comment-media-info.ts index 02a99527..bf646401 100644 --- a/src/hooks/use-comment-media-info.ts +++ b/src/hooks/use-comment-media-info.ts @@ -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); }; diff --git a/src/hooks/use-popular-posts.ts b/src/hooks/use-popular-posts.ts index aa7e54ce..df65f928 100644 --- a/src/hooks/use-popular-posts.ts +++ b/src/hooks/use-popular-posts.ts @@ -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 ( diff --git a/src/lib/utils/media-utils.ts b/src/lib/utils/media-utils.ts index f6cfdfb3..816e98e9 100644 --- a/src/lib/utils/media-utils.ts +++ b/src/lib/utils/media-utils.ts @@ -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 = } }; -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 }); diff --git a/src/stores/use-catalog-filters-store.ts b/src/stores/use-catalog-filters-store.ts index c2b1fde4..ef72762f 100644 --- a/src/stores/use-catalog-filters-store.ts +++ b/src/stores/use-catalog-filters-store.ts @@ -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() || ''; diff --git a/src/views/board/board.tsx b/src/views/board/board.tsx index 16f13856..2c2e99ff 100644 --- a/src/views/board/board.tsx +++ b/src/views/board/board.tsx @@ -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) diff --git a/src/views/catalog/catalog.tsx b/src/views/catalog/catalog.tsx index 88e944a3..79ebd77f 100644 --- a/src/views/catalog/catalog.tsx +++ b/src/views/catalog/catalog.tsx @@ -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) diff --git a/src/views/home/popular-threads-box/popular-threads-box.tsx b/src/views/home/popular-threads-box/popular-threads-box.tsx index 4940359d..37d86233 100644 --- a/src/views/home/popular-threads-box/popular-threads-box.tsx +++ b/src/views/home/popular-threads-box/popular-threads-box.tsx @@ -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 (