/* Copyright (C) 2025 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import React, { useEffect, useRef, useState } from 'react'; /** 视口外预加载边距,减少滚动时出现空白 */ const ROOT_MARGIN = '280px 0px 280px 0px'; /** * 体验馆消息气泡:进入(或接近)可视区域后再挂载子树,减轻长对话下 DOM/媒体压力。 * variant=media:更高占位,避免图片/视频模式切换后列表高度不足导致滚不到底部。 */ const LazyVisibleMessage = ({ messageId, className, variant = 'default', children, }) => { const [visible, setVisible] = useState(false); const rootRef = useRef(null); useEffect(() => { const rootEl = rootRef.current; if (!rootEl || visible) { return undefined; } const observer = new IntersectionObserver( (entries) => { for (const entry of entries) { if (entry.isIntersecting) { setVisible(true); break; } } }, { root: null, rootMargin: ROOT_MARGIN, threshold: 0 }, ); observer.observe(rootEl); return () => observer.disconnect(); }, [messageId, visible]); const placeholderClass = variant === 'media' ? 'rounded-lg min-h-[220px] sm:min-h-[280px] w-full max-w-3xl bg-semi-color-fill-0 animate-pulse' : 'rounded-lg min-h-[72px] max-w-3xl bg-semi-color-fill-0 animate-pulse'; return (
{visible ? ( children ) : (
)}
); }; export default LazyVisibleMessage;