fix: recalculate message overflow when image loads

The detection if the read-more button is necessary happened once at
render and wasn't recalculated after images loaded. This caused hidden
message overflow without a read more button.
This commit is contained in:
Jannis Mattheis 2025-09-20 13:18:59 +02:00
parent 4329e571ad
commit f1bf24c10f
2 changed files with 30 additions and 8 deletions

View File

@ -2,6 +2,16 @@ import React from 'react';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import gfm from 'remark-gfm'; import gfm from 'remark-gfm';
export const Markdown = ({children}: {children: string}) => ( export const Markdown = ({
<ReactMarkdown remarkPlugins={[gfm]}>{children}</ReactMarkdown> children,
onImageLoaded = () => {},
}: {
children: string;
onImageLoaded?: () => void;
}) => (
<ReactMarkdown
components={{img: ({...props}) => <img onLoad={onImageLoaded} {...props} />}}
remarkPlugins={[gfm]}>
{children}
</ReactMarkdown>
); );

View File

@ -127,15 +127,27 @@ const Message = ({
expanded: initialExpanded, expanded: initialExpanded,
}: IProps) => { }: IProps) => {
const theme = useTheme(); const theme = useTheme();
const [previewRef, setPreviewRef] = React.useState<HTMLDivElement | null>(null); const contentRef = React.useRef<HTMLDivElement | null>(null);
const {classes} = useStyles(); const {classes} = useStyles();
const [expanded, setExpanded] = React.useState(initialExpanded); const [expanded, setExpanded] = React.useState(initialExpanded);
const [isOverflowing, setOverflowing] = React.useState(false); const [isOverflowing, setOverflowing] = React.useState(false);
const smallHeader = useMediaQuery(theme.breakpoints.down('md')); const smallHeader = useMediaQuery(theme.breakpoints.down('md'));
React.useEffect(() => { const refreshOverflowing = React.useCallback(() => {
setOverflowing(!!previewRef && previewRef.scrollHeight > previewRef.clientHeight); const ref = contentRef.current;
}, [previewRef]); if (!ref) {
return;
}
setOverflowing((overflowing) => overflowing || ref.scrollHeight > ref.clientHeight);
}, [contentRef, setOverflowing]);
const onContentRef = React.useCallback(
(ref: HTMLDivElement | null) => {
contentRef.current = ref;
refreshOverflowing();
},
[contentRef, refreshOverflowing]
);
React.useEffect(() => void onExpand(expanded), [expanded]); React.useEffect(() => void onExpand(expanded), [expanded]);
@ -144,7 +156,7 @@ const Message = ({
const renderContent = () => { const renderContent = () => {
switch (contentType(extras)) { switch (contentType(extras)) {
case RenderMode.Markdown: case RenderMode.Markdown:
return <Markdown>{content}</Markdown>; return <Markdown onImageLoaded={refreshOverflowing}>{content}</Markdown>;
case RenderMode.Plain: case RenderMode.Plain:
default: default:
return <span className={classes.plainContent}>{content}</span>; return <span className={classes.plainContent}>{content}</span>;
@ -181,7 +193,7 @@ const Message = ({
<div className={classes.messageContentWrapper}> <div className={classes.messageContentWrapper}>
<Typography <Typography
component="div" component="div"
ref={setPreviewRef} ref={onContentRef}
className={`${classes.content} content ${ className={`${classes.content} content ${
isOverflowing && expanded ? 'expanded' : '' isOverflowing && expanded ? 'expanded' : ''
}`}> }`}>