import clsx from "clsx"; import { memo, useEffect, useRef, useState } from "react"; import useCurrentUser from "@/hooks/useCurrentUser"; import { useMemoStore } from "@/store/v1"; import { Node, NodeType } from "@/types/proto/api/v1/markdown_service"; import { useTranslate } from "@/utils/i18n"; import { isSuperUser } from "@/utils/user"; import Renderer from "./Renderer"; import { RendererContext } from "./types"; // MAX_DISPLAY_HEIGHT is the maximum height of the memo content to display in compact mode. const MAX_DISPLAY_HEIGHT = 256; interface Props { nodes: Node[]; memoName?: string; compact?: boolean; readonly?: boolean; disableFilter?: boolean; // embeddedMemos is a set of memo resource names that are embedded in the current memo. // This is used to prevent infinite loops when a memo embeds itself. embeddedMemos?: Set; className?: string; contentClassName?: string; onClick?: (e: React.MouseEvent) => void; onDoubleClick?: (e: React.MouseEvent) => void; } const MemoContent: React.FC = (props: Props) => { const { className, contentClassName, nodes, memoName, embeddedMemos, onClick, onDoubleClick } = props; const t = useTranslate(); const currentUser = useCurrentUser(); const memoStore = useMemoStore(); const memoContentContainerRef = useRef(null); const [showCompactMode, setShowCompactMode] = useState(false); const memo = memoName ? memoStore.getMemoByName(memoName) : null; const allowEdit = !props.readonly && memo && (currentUser?.name === memo.creator || isSuperUser(currentUser)); // Initial compact mode. useEffect(() => { if (!props.compact) { return; } if (!memoContentContainerRef.current) { return; } if ((memoContentContainerRef.current as HTMLDivElement).getBoundingClientRect().height > MAX_DISPLAY_HEIGHT) { setShowCompactMode(true); } }, []); const handleMemoContentClick = async (e: React.MouseEvent) => { if (onClick) { onClick(e); } }; const handleMemoContentDoubleClick = async (e: React.MouseEvent) => { if (onDoubleClick) { onDoubleClick(e); } }; let prevNode: Node | null = null; let skipNextLineBreakFlag = false; return (
{nodes.map((node, index) => { if (prevNode?.type !== NodeType.LINE_BREAK && node.type === NodeType.LINE_BREAK && skipNextLineBreakFlag) { skipNextLineBreakFlag = false; return null; } prevNode = node; skipNextLineBreakFlag = true; return ; })} {showCompactMode && (
)}
{showCompactMode && (
setShowCompactMode(false)} > {t("memo.show-more")}
)}
); }; export default memo(MemoContent);