/** * MessageActionSheet -- Mobile long-press context menu. * Dims the screen, highlights the message in-place, and shows * an action bar with edit/delete + emoji reactions. */ import { useEffect, useCallback, useState } from 'react' import type { MessageRect } from './MessageItem' interface MessageActionSheetProps { visible: boolean onClose: () => void isOwn: boolean onReaction: (emoji: string) => void onEdit: () => void onDelete: () => void onReply: () => void messageContent: string messageRect: MessageRect | null } const ACTION_BAR_HEIGHT = 44 const VIEWPORT_PADDING = 12 const EMOJI_LIST = ['👍', '👎', '😄', '🎉', '❤️', '🚀', '👀', '🔥', '💯', '🤔', '😢', '🙏'] export function MessageActionSheet({ visible, onClose, isOwn, onReaction, onEdit, onDelete, onReply, messageContent, messageRect, }: MessageActionSheetProps) { const [layout, setLayout] = useState<'above' | 'below'>('above') const handleAction = useCallback( (action: () => void) => { action() onClose() }, [onClose], ) useEffect(() => { if (!visible || !messageRect) return const spaceAbove = messageRect.top const needed = ACTION_BAR_HEIGHT + VIEWPORT_PADDING setLayout(spaceAbove >= needed ? 'above' : 'below') }, [visible, messageRect]) useEffect(() => { if (!visible) return function handleKeyDown(e: KeyboardEvent) { if (e.key === 'Escape') onClose() } document.addEventListener('keydown', handleKeyDown) document.body.style.overflow = 'hidden' return () => { document.removeEventListener('keydown', handleKeyDown) document.body.style.overflow = '' } }, [visible, onClose]) if (!visible || !messageRect) return null const actionBar = (
{isOwn && ( <>
)}
{EMOJI_LIST.map((emoji) => ( ))}
) return (
e.stopPropagation()} > {layout === 'above' && (
{actionBar}
)}

{messageContent}

{layout === 'below' &&
{actionBar}
}
) }