diff --git a/src/app/components/message/MsgTypeRenderers.tsx b/src/app/components/message/MsgTypeRenderers.tsx index f7cbc4811..89c9e11c1 100644 --- a/src/app/components/message/MsgTypeRenderers.tsx +++ b/src/app/components/message/MsgTypeRenderers.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, useState, useMemo, useCallback } from 'react'; import { Box, Chip, Icon, Icons, Text, toRem } from 'folds'; import { IContent } from 'matrix-js-sdk'; import { JUMBO_EMOJI_REG, URL_REG } from '../../utils/regex'; @@ -27,6 +27,9 @@ import { FALLBACK_MIMETYPE, getBlobSafeMimeType } from '../../utils/mimeTypes'; import { parseGeoUri, scaleYDimension } from '../../utils/common'; import { Attachment, AttachmentBox, AttachmentContent, AttachmentHeader } from './attachment'; import { FileHeader } from './FileHeader'; +import { Button } from 'folds'; + +const CHARACTER_LIMIT = 750; export function MBadEncrypted() { return ( @@ -63,6 +66,11 @@ export function BrokenContent() { ); } +const truncateText = (text: string, limit: number) => { + if (text.length <= limit) return text; + return `${text.slice(0, limit).trim()}...`; +}; + type RenderBodyProps = { body: string; customBody?: string; @@ -80,6 +88,22 @@ export function MText({ edited, content, renderBody, renderUrlsPreview }: MTextP const trimmedBody = trimReplyFromBody(body); const urlsMatch = renderUrlsPreview && trimmedBody.match(URL_REG); const urls = urlsMatch ? [...new Set(urlsMatch)] : undefined; + const [isExpanded, setIsExpanded] = useState(false); + + const { shouldTruncate, finalContent, finalCustomContent } = useMemo(() => { + const contentStr = trimmedBody || ''; + const customContentStr = typeof customBody === 'string' ? trimReplyFromBody(customBody) : ''; + const needTruncate = contentStr.length > 750 || customContentStr.length > 750; + return { + contentStr, + customContentStr, + shouldTruncate: needTruncate, + finalContent: isExpanded ? contentStr : truncateText(contentStr, CHARACTER_LIMIT), + finalCustomContent: isExpanded + ? customContentStr + : truncateText(customContentStr, CHARACTER_LIMIT), + }; + }, [trimmedBody, customBody, isExpanded]); return ( <> @@ -88,12 +112,22 @@ export function MText({ edited, content, renderBody, renderUrlsPreview }: MTextP jumboEmoji={JUMBO_EMOJI_REG.test(trimmedBody)} > {renderBody({ - body: trimmedBody, - customBody: typeof customBody === 'string' ? customBody : undefined, + body: finalContent, + customBody: typeof customBody === 'string' ? finalCustomContent : undefined, })} {edited && } {renderUrlsPreview && urls && urls.length > 0 && renderUrlsPreview(urls)} + + {shouldTruncate && ( + + )} ); } diff --git a/src/index.scss b/src/index.scss index 1c223051d..9553b0e84 100644 --- a/src/index.scss +++ b/src/index.scss @@ -495,6 +495,20 @@ textarea { color: inherit; word-spacing: inherit; } + +.show-more { + cursor: pointer; + color: #5e9ecf; + padding: 5px; + border-radius: 5px; + text-decoration: underline; + +} + +.show-more:hover { + color: #085fa7; +} + .noselect { -webkit-touch-callout: none; /* iOS Safari */ -webkit-user-select: none; /* Safari */