'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}
);
}