'use client'; import { CheckCircle2, Copy, Forward, Pencil, Pin, PinOff, Reply, Trash2, XCircle } from 'lucide-react'; import { ChatMessageReactions } from '@/components/chat/chat-message-reactions'; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger } from '@/components/ui/context-menu'; import { suppressChatDragSelection } from '@/hooks/use-chat-drag-selection'; import type { ChatMessage } from '@/lib/api'; import { QUICK_REACTIONS, canDeleteMessage, canEditMessage, getMessageCopyText } from '@/lib/chat-message-utils'; import { cn } from '@/lib/utils'; interface ChatMessageContextMenuProps { message: ChatMessage; userId?: string; isE2E?: boolean; visibleText?: string; children: React.ReactNode; onEdit?: () => void; onDelete?: () => void; onReply?: () => void; onForward?: () => void; onTogglePin?: () => void; onSelect?: () => void; onToggleReaction?: (emoji: string) => void; onCopy?: () => void; } export function ChatMessageContextMenu({ message, userId, isE2E, visibleText, children, onEdit, onDelete, onReply, onForward, onTogglePin, onSelect, onToggleReaction, onCopy }: ChatMessageContextMenuProps) { const editable = canEditMessage(message, userId, isE2E); const deletable = canDeleteMessage(message, userId); const copyText = getMessageCopyText(message, visibleText); const disabled = message.isDeleted; function runMenuAction(action?: () => void) { suppressChatDragSelection(); action?.(); } return ( { if (open) suppressChatDragSelection(800); }} > {children} {!disabled ? ( <>
{QUICK_REACTIONS.map((emoji) => ( ))}
) : null} {onReply && !disabled ? ( { event.preventDefault(); runMenuAction(onReply); }} > Ответить ) : null} {editable ? ( { event.preventDefault(); runMenuAction(onEdit); }} > Изменить ) : null} {copyText ? ( { event.preventDefault(); runMenuAction(() => { void navigator.clipboard.writeText(copyText); onCopy?.(); }); }} > Копировать текст ) : null} {onForward && !disabled && !message.isEncrypted ? ( { event.preventDefault(); runMenuAction(onForward); }} > Переслать ) : null} {onTogglePin && !disabled ? ( { event.preventDefault(); runMenuAction(onTogglePin); }} > {message.isPinned ? : } {message.isPinned ? 'Открепить' : 'Закрепить'} ) : null} {deletable ? ( { event.preventDefault(); runMenuAction(onDelete); }} > Удалить ) : null} {onSelect && !disabled ? ( <> { event.preventDefault(); runMenuAction(onSelect); }} > Выделить ) : null} {!disabled && message.reactions?.length ? (
) : null}
); } interface ChatSelectionContextMenuProps { count: number; canPin?: boolean; pinned?: boolean; children: React.ReactNode; onReply?: () => void; onCopy?: () => void; onForward?: () => void; onDelete?: () => void; onTogglePin?: () => void; onCancel?: () => void; } export function ChatSelectionContextMenu({ count, canPin, pinned, children, onReply, onCopy, onForward, onDelete, onTogglePin, onCancel }: ChatSelectionContextMenuProps) { function runMenuAction(action?: () => void) { suppressChatDragSelection(); action?.(); } return ( { if (open) suppressChatDragSelection(800); }} > {children} { event.preventDefault(); runMenuAction(onReply); }} > Ответить {canPin && onTogglePin ? ( { event.preventDefault(); runMenuAction(onTogglePin); }} > {pinned ? : } {pinned ? 'Открепить' : 'Закрепить'} ) : null} { event.preventDefault(); runMenuAction(onCopy); }} > Копировать выбранное как текст { event.preventDefault(); runMenuAction(onForward); }} > Переслать выбранное { event.preventDefault(); runMenuAction(onDelete); }} > Удалить выбранное { event.preventDefault(); runMenuAction(onCancel); }} > Отменить выбор Выбрано сообщений: {count} ); } interface SelectableMessageWrapperProps { selected?: boolean; selectionMode?: boolean; align?: 'left' | 'right'; onClick?: () => void; className?: string; children: React.ReactNode; } export function SelectableMessageWrapper({ selected, selectionMode, align = 'left', onClick, className, children }: SelectableMessageWrapperProps) { return (
{selectionMode ? ( ) : null}
{children}
); }