mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-05-13 15:10:05 +00:00
fix: Reply Setting
This commit is contained in:
@@ -2549,6 +2549,25 @@
|
||||
}
|
||||
|
||||
// Ghost preview — appears on hover, frosted glass
|
||||
&.preview-below {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 0;
|
||||
|
||||
.reply-ghost {
|
||||
top: calc(100% + 6px);
|
||||
bottom: auto;
|
||||
transform: translateY(-4px) scale(0.98);
|
||||
}
|
||||
|
||||
&:hover .reply-ghost {
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
&.preview-above {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.reply-ghost {
|
||||
position: absolute;
|
||||
bottom: calc(100% + 6px);
|
||||
|
||||
@@ -1510,6 +1510,7 @@ function ChatPage(props: ChatPageProps) {
|
||||
const [groupMemberSearchKeyword, setGroupMemberSearchKeyword] = useState('')
|
||||
const [copiedField, setCopiedField] = useState<string | null>(null)
|
||||
const [highlightedMessageKeys, setHighlightedMessageKeys] = useState<string[]>([])
|
||||
const [quoteLayout, setQuoteLayout] = useState<configService.QuoteLayout>('quote-top')
|
||||
const [isRefreshingSessions, setIsRefreshingSessions] = useState(false)
|
||||
const [foldedView, setFoldedView] = useState(false) // 是否在"折叠的群聊"视图
|
||||
const [bizView, setBizView] = useState(false) // 是否在"公众号"视图
|
||||
@@ -3066,6 +3067,33 @@ function ChatPage(props: ChatPageProps) {
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
let canceled = false
|
||||
const loadQuoteLayout = () => {
|
||||
void configService.getQuoteLayout()
|
||||
.then((layout) => {
|
||||
if (!canceled) setQuoteLayout(layout)
|
||||
})
|
||||
.catch(() => {
|
||||
if (!canceled) setQuoteLayout('quote-top')
|
||||
})
|
||||
}
|
||||
|
||||
loadQuoteLayout()
|
||||
const handleFocus = () => loadQuoteLayout()
|
||||
const handleQuoteLayoutChanged = (event: Event) => {
|
||||
const layout = (event as CustomEvent<configService.QuoteLayout>).detail
|
||||
setQuoteLayout(layout === 'quote-bottom' ? 'quote-bottom' : 'quote-top')
|
||||
}
|
||||
window.addEventListener('focus', handleFocus)
|
||||
window.addEventListener('quote-layout-changed', handleQuoteLayoutChanged)
|
||||
return () => {
|
||||
canceled = true
|
||||
window.removeEventListener('focus', handleFocus)
|
||||
window.removeEventListener('quote-layout-changed', handleQuoteLayoutChanged)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
void (async () => {
|
||||
@@ -6820,6 +6848,7 @@ function ChatPage(props: ChatPageProps) {
|
||||
myAvatarUrl={myAvatarUrl}
|
||||
myWxid={myWxid}
|
||||
isGroupChat={isCurrentSessionGroup}
|
||||
quoteLayout={quoteLayout}
|
||||
autoTranscribeVoiceEnabled={autoTranscribeVoiceEnabled}
|
||||
onRequireModelDownload={handleRequireModelDownload}
|
||||
onContextMenu={handleContextMenu}
|
||||
@@ -6840,6 +6869,7 @@ function ChatPage(props: ChatPageProps) {
|
||||
myAvatarUrl,
|
||||
myWxid,
|
||||
isCurrentSessionGroup,
|
||||
quoteLayout,
|
||||
autoTranscribeVoiceEnabled,
|
||||
handleRequireModelDownload,
|
||||
handleContextMenu,
|
||||
@@ -8395,6 +8425,7 @@ function MessageBubble({
|
||||
myAvatarUrl,
|
||||
myWxid,
|
||||
isGroupChat,
|
||||
quoteLayout,
|
||||
autoTranscribeVoiceEnabled,
|
||||
onRequireModelDownload,
|
||||
onContextMenu,
|
||||
@@ -8410,6 +8441,7 @@ function MessageBubble({
|
||||
myAvatarUrl?: string;
|
||||
myWxid?: string;
|
||||
isGroupChat?: boolean;
|
||||
quoteLayout: configService.QuoteLayout;
|
||||
autoTranscribeVoiceEnabled?: boolean;
|
||||
onRequireModelDownload?: (sessionId: string, messageId: string) => void;
|
||||
onContextMenu?: (e: React.MouseEvent, message: Message) => void;
|
||||
@@ -9665,17 +9697,26 @@ function MessageBubble({
|
||||
event.stopPropagation()
|
||||
onJumpToQuotedMessage(quotedJumpTarget)
|
||||
}, [isSelectionMode, onJumpToQuotedMessage, quotedJumpTarget])
|
||||
// Ambient Reply: single fixed layout (anchor above, message below)
|
||||
const isQuoteBelow = quoteLayout === 'quote-bottom'
|
||||
const renderBubbleWithQuote = useCallback((quotedNode: React.ReactNode, messageNode: React.ReactNode) => (
|
||||
<div className="bubble-content">
|
||||
{quotedNode}
|
||||
{messageNode}
|
||||
<div className={`bubble-content ${isQuoteBelow ? 'quote-layout-bottom' : 'quote-layout-top'}`}>
|
||||
{isQuoteBelow ? (
|
||||
<>
|
||||
{messageNode}
|
||||
{quotedNode}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{quotedNode}
|
||||
{messageNode}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
), [])
|
||||
), [isQuoteBelow])
|
||||
|
||||
// Ambient Reply: render reply-anchor + ghost preview
|
||||
const renderQuotedMessageBlock = useCallback((contentNode: React.ReactNode) => (
|
||||
<div className="ambient-reply-wrapper">
|
||||
<div className={`ambient-reply-wrapper ${isQuoteBelow ? 'preview-below' : 'preview-above'}`}>
|
||||
{/* Reply anchor - always visible, subtle */}
|
||||
<div
|
||||
className={`reply-anchor ${quotedJumpTarget ? 'jumpable' : ''}`}
|
||||
@@ -9698,7 +9739,7 @@ function MessageBubble({
|
||||
<div className="reply-ghost-text">{contentNode}</div>
|
||||
</div>
|
||||
</div>
|
||||
), [displayQuotedSenderName, handleQuotedJumpClick, handleQuotedJumpKeyDown, isSelectionMode, quotedJumpTarget])
|
||||
), [displayQuotedSenderName, handleQuotedJumpClick, handleQuotedJumpKeyDown, isQuoteBelow, isSelectionMode, quotedJumpTarget])
|
||||
|
||||
const handlePlayVideo = useCallback(async () => {
|
||||
if (!videoInfo?.videoUrl) return
|
||||
@@ -10893,6 +10934,7 @@ const MemoMessageBubble = React.memo(MessageBubble, (prevProps, nextProps) => {
|
||||
if (prevProps.myAvatarUrl !== nextProps.myAvatarUrl) return false
|
||||
if (prevProps.myWxid !== nextProps.myWxid) return false
|
||||
if (prevProps.isGroupChat !== nextProps.isGroupChat) return false
|
||||
if (prevProps.quoteLayout !== nextProps.quoteLayout) return false
|
||||
if (prevProps.autoTranscribeVoiceEnabled !== nextProps.autoTranscribeVoiceEnabled) return false
|
||||
if (prevProps.isSelectionMode !== nextProps.isSelectionMode) return false
|
||||
if (prevProps.isSelected !== nextProps.isSelected) return false
|
||||
|
||||
@@ -1698,6 +1698,7 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) {
|
||||
if (selected) return
|
||||
setQuoteLayout(option.value)
|
||||
await configService.setQuoteLayout(option.value)
|
||||
window.dispatchEvent(new CustomEvent('quote-layout-changed', { detail: option.value }))
|
||||
showMessage(option.successMessage, true)
|
||||
}}
|
||||
role="radio"
|
||||
|
||||
Reference in New Issue
Block a user