diff --git a/src/components/comment-media/__tests__/comment-media.test.tsx b/src/components/comment-media/__tests__/comment-media.test.tsx index d5407c05..8ebcd1b2 100644 --- a/src/components/comment-media/__tests__/comment-media.test.tsx +++ b/src/components/comment-media/__tests__/comment-media.test.tsx @@ -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: { diff --git a/src/components/comment-media/comment-media.tsx b/src/components/comment-media/comment-media.tsx index 490ecf1d..4f1bc0c3 100644 --- a/src/components/comment-media/comment-media.tsx +++ b/src/components/comment-media/comment-media.tsx @@ -210,7 +210,7 @@ const Media = ({ commentMediaInfo, disableToggle, isReply, setShowThumbnail }: M const mediaDimensions = getMediaDimensions(commentMediaInfo); return ( - + {type === 'iframe' && 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 {hasError ? ( File deleted @@ -379,7 +382,8 @@ const Image = ({ commentMediaInfo, disableToggle = false, displayHeight, display ) : ( {hasError ? ( File deleted diff --git a/src/lib/utils/__tests__/pretext-height-estimates.test.ts b/src/lib/utils/__tests__/pretext-height-estimates.test.ts index 03c5f6f3..076d2e46 100644 --- a/src/lib/utils/__tests__/pretext-height-estimates.test.ts +++ b/src/lib/utils/__tests__/pretext-height-estimates.test.ts @@ -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, diff --git a/src/lib/utils/measurement-attributes.ts b/src/lib/utils/measurement-attributes.ts new file mode 100644 index 00000000..a4d3c344 --- /dev/null +++ b/src/lib/utils/measurement-attributes.ts @@ -0,0 +1 @@ +export const EXPANDED_MEDIA_DATA_ATTRIBUTE = 'data-expanded-media'; diff --git a/src/lib/utils/pretext-height-estimates.ts b/src/lib/utils/pretext-height-estimates.ts index 43e72212..35c1efbb 100644 --- a/src/lib/utils/pretext-height-estimates.ts +++ b/src/lib/utils/pretext-height-estimates.ts @@ -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;