import type { MessageWithMeta } from '@/contexts'; import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { useUser } from '@/contexts'; import { cn } from '@/lib/utils'; import { AlertCircle, Copy, Edit, GitPullRequest, LayoutDashboard, MoreHorizontal, Reply, Trash2 } from 'lucide-react'; import { useState } from 'react'; import { toast } from 'sonner'; import { getSenderUserUid } from './sender'; interface RoomMessageActionsProps { message: MessageWithMeta; onEdit?: () => void; onRevoke?: () => void; onReply?: () => void; } export function RoomMessageActions({ message, onEdit, onRevoke, onReply }: RoomMessageActionsProps) { const [isOpen, setIsOpen] = useState(false); const { user } = useUser(); const isOwner = user?.uid === getSenderUserUid(message); const handleCopy = async () => { if (message.content_type !== 'text') return; try { await navigator.clipboard.writeText(message.content); toast.success('Message copied'); } catch { toast.error('Failed to copy message'); } finally { setIsOpen(false); } }; return (
{onReply && ( )} {message.content_type === 'text' && ( Copy )} {message.content_type === 'text' && ( { toast.info('Creating issue from message…', { description: 'This will open the issue creation form with the message content pre-filled.', action: { label: 'Create', onClick: () => { // TODO: wire to POST /api/issue/{project}/issues/from-message const title = message.content.split('\n')[0].slice(0, 80); const body = `Converted from room message (${message.id})\n\n${message.content}`; const params = new URLSearchParams({ title, body }); window.open(`/project/-/issues/new?${params}`, '_blank'); }, }, }); setIsOpen(false); }} > Create Issue )} {message.content_type === 'text' && /\n```[\s\S]*?\n```/.test(message.content) && ( { setIsOpen(false); toast.info('Creating PR from message…', { description: 'Open the PR creation form with the code snippet pre-filled.', action: { label: 'Open', onClick: () => { // TODO: wire to POST /api/repo_pr/{ns}/{repo}/pulls/from-message const params = new URLSearchParams({ body: `Converted from room message (${message.id})\n\n${message.content}`, }); window.open(`/pulls/new?${params}`, '_blank'); }, }, }); }} > Create PR )} {message.content_type === 'text' && ( { setIsOpen(false); toast.info('Adding to board…', { description: 'Open the kanban board with this message as the card description.', action: { label: 'Open Board', onClick: () => { // TODO: wire to POST /api/board/cards const params = new URLSearchParams({ title: message.content.split('\n')[0].slice(0, 80), description: message.content, }); window.open(`/boards/new?${params}`, '_blank'); }, }, }); }} > Add to Board )} {isOwner && onEdit && ( <> { onEdit(); setIsOpen(false); }} > Edit )} {isOwner && onRevoke && ( <> { onRevoke(); setIsOpen(false); }} > Delete )}
); }