fix(feed): measure expanded reply media height

This commit is contained in:
Tommaso Casaburi
2026-04-27 13:30:38 +07:00
parent 4fe0494be6
commit b0b5d45376
5 changed files with 49 additions and 3 deletions
@@ -128,6 +128,29 @@ describe('CommentMedia', () => {
expect(container.textContent).toContain('640x480');
});
it('marks expanded reply images so virtualized feed rows use live height', async () => {
await renderMedia({
commentMediaInfo: {
linkHeight: 300,
linkWidth: 600,
type: 'image',
url: 'https://cdn.example.com/reply-image.jpg',
},
parentCid: 'parent-cid',
setShowThumbnail: setShowThumbnailMock,
showThumbnail: true,
});
expect(container.querySelector('[data-expanded-media="true"]')).toBeNull();
const image = container.querySelector('img[src="https://cdn.example.com/reply-image.jpg"]');
await act(async () => {
image?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
});
expect(container.querySelector('[data-expanded-media="true"]')).toBeTruthy();
});
it('falls back to the deleted-file placeholder when an image fails to load', async () => {
await renderMedia({
commentMediaInfo: {
@@ -210,7 +210,7 @@ const Media = ({ commentMediaInfo, disableToggle, isReply, setShowThumbnail }: M
const mediaDimensions = getMediaDimensions(commentMediaInfo);
return (
<span className={mediaClass}>
<span className={mediaClass} data-expanded-media='true'>
{type === 'iframe' && url ? (
<Embed url={url} />
) : type === 'gif' ? (
@@ -307,6 +307,8 @@ const Image = ({ commentMediaInfo, disableToggle = false, displayHeight, display
}`;
const thumbnailSmallPadding = isMobile ? styles.thumbnailMobile : styles.thumbnailReplyDesktop;
const thumbnailDimensions = { '--width': displayWidth, '--height': displayHeight } as React.CSSProperties;
const expandedMediaAttribute = isImageExpanded ? 'true' : undefined;
const imageMediaStyle = isImageExpanded ? undefined : thumbnailDimensions;
const [hasError, setHasError] = useState(false);
const handleError = () => setHasError(true);
@@ -340,7 +342,8 @@ const Image = ({ commentMediaInfo, disableToggle = false, displayHeight, display
<span className={`${styles.thumbnail} ${isImageExpanded && isMobile ? styles.removeFloat : ''}`}>
<span
className={isImageExpanded ? mediaClass : `${isOutOfFeed ? styles.communityAvatar : styles.thumbnailSmall} ${thumbnailSmallPadding}`}
style={isImageExpanded ? {} : thumbnailDimensions}
data-expanded-media={expandedMediaAttribute}
style={imageMediaStyle}
>
{hasError ? (
<img src='assets/filedeleted-res.gif' alt='File deleted' />
@@ -379,7 +382,8 @@ const Image = ({ commentMediaInfo, disableToggle = false, displayHeight, display
) : (
<span
className={isImageExpanded ? mediaClass : `${isOutOfFeed ? styles.communityAvatar : styles.thumbnailBig} ${styles.thumbnail}`}
style={isImageExpanded ? {} : thumbnailDimensions}
data-expanded-media={expandedMediaAttribute}
style={imageMediaStyle}
>
{hasError ? (
<img src='assets/filedeleted-res.gif' alt='File deleted' />
@@ -7,6 +7,7 @@ import {
resolveFeedVirtualizationMode,
resolveReplyVirtualizationMode,
} from '../pretext-height-estimates';
import { EXPANDED_MEDIA_DATA_ATTRIBUTE } from '../measurement-attributes';
describe('pretext-height-estimates', () => {
beforeEach(() => {
@@ -81,6 +82,18 @@ describe('pretext-height-estimates', () => {
expect(getReplyItemSizeFromElement(element, 'offsetHeight')).toBe(222);
});
it('uses live DOM height when expanded media is inside an estimated item', () => {
const element = document.createElement('div');
const child = document.createElement('div');
Object.defineProperty(element, 'offsetHeight', { configurable: true, value: 421 });
element.dataset.pretextHeight = '123';
child.setAttribute(EXPANDED_MEDIA_DATA_ATTRIBUTE, 'true');
element.appendChild(child);
expect(getReplyItemSizeFromElement(element, 'offsetHeight')).toBe(421);
});
it('adds desktop board-label height for multiboard text posts without media', () => {
const metrics = {
abbrFontSizePx: 13,
+1
View File
@@ -0,0 +1 @@
export const EXPANDED_MEDIA_DATA_ATTRIBUTE = 'data-expanded-media';
@@ -1,6 +1,7 @@
import type { Comment } from '@bitsocial/bitsocial-react-hooks';
import { layout, layoutNextLine, prepare, prepareWithSegments } from '@chenglou/pretext';
import { getCommentMediaInfo, getHasThumbnail } from './media-utils';
import { EXPANDED_MEDIA_DATA_ATTRIBUTE } from './measurement-attributes';
import { removeMarkdown } from './post-utils';
import { getRenderableMobileBacklinks } from './reply-backlink-utils';
@@ -839,6 +840,10 @@ export const getPretextItemSizeFromElement = (element: HTMLElement, field: 'offs
return element.offsetWidth;
}
if (element.dataset.expandedMedia === 'true' || element.querySelector(`[${EXPANDED_MEDIA_DATA_ATTRIBUTE}="true"]`)) {
return element.offsetHeight;
}
const ownEstimatedHeight = Number.parseFloat(element.dataset.pretextHeight || '');
if (Number.isFinite(ownEstimatedHeight) && ownEstimatedHeight > 0) {
return ownEstimatedHeight;