@@ -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 (