"use client"; import { useTranslations } from "next-intl"; import { Email, Mailbox } from "@/lib/jmap/types"; import { ContextMenu, ContextMenuItem, ContextMenuSeparator, ContextMenuSubMenu, ContextMenuHeader, } from "@/components/ui/context-menu"; import { Reply, ReplyAll, Forward, Mail, MailOpen, Star, Trash2, Archive, FolderInput, Palette, X, Inbox, Send, File, Folder, ShieldAlert, ShieldCheck, } from "lucide-react"; import { cn } from "@/lib/utils"; interface Position { x: number; y: number; } interface EmailContextMenuProps { email: Email; position: Position; isOpen: boolean; onClose: () => void; menuRef: React.RefObject; mailboxes: Mailbox[]; selectedMailbox: string; currentMailboxRole?: string; isMultiSelect?: boolean; selectedCount?: number; // Single email actions onReply?: () => void; onReplyAll?: () => void; onForward?: () => void; onMarkAsRead?: (read: boolean) => void; onToggleStar?: () => void; onDelete?: () => void; onArchive?: () => void; onSetColorTag?: (color: string | null) => void; onMoveToMailbox?: (mailboxId: string) => void; onMarkAsSpam?: () => void; onUndoSpam?: () => void; // Batch actions onBatchMarkAsRead?: (read: boolean) => void; onBatchDelete?: () => void; onBatchMoveToMailbox?: (mailboxId: string) => void; onBatchMarkAsSpam?: () => void; onBatchUndoSpam?: () => void; } // Get mailbox icon based on role const getMailboxIcon = (role?: string) => { switch (role) { case "inbox": return Inbox; case "sent": return Send; case "drafts": return File; case "trash": return Trash2; case "archive": return Archive; default: return Folder; } }; // Get current color from email keywords const getCurrentColor = (keywords: Record | undefined) => { if (!keywords) return null; for (const key of Object.keys(keywords)) { if (key.startsWith("$color:") && keywords[key] === true) { return key.replace("$color:", ""); } } return null; }; export function EmailContextMenu({ email, position, isOpen, onClose, menuRef, mailboxes, selectedMailbox, currentMailboxRole, isMultiSelect = false, selectedCount = 1, onReply, onReplyAll, onForward, onMarkAsRead, onToggleStar, onDelete, onArchive, onSetColorTag, onMoveToMailbox, onMarkAsSpam, onUndoSpam, onBatchMarkAsRead, onBatchDelete, onBatchMoveToMailbox, onBatchMarkAsSpam, onBatchUndoSpam, }: EmailContextMenuProps) { const t = useTranslations("context_menu"); const tColor = useTranslations("email_viewer.color_tag"); const isUnread = !email.keywords?.$seen; const isStarred = email.keywords?.$flagged; const currentColor = getCurrentColor(email.keywords); const showBatchActions = isMultiSelect && selectedCount > 1; const isInJunkFolder = currentMailboxRole === 'junk'; // Color options for email tags (using translations) const colorOptions = [ { name: tColor("red"), value: "red", color: "bg-red-500" }, { name: tColor("orange"), value: "orange", color: "bg-orange-500" }, { name: tColor("yellow"), value: "yellow", color: "bg-yellow-500" }, { name: tColor("green"), value: "green", color: "bg-green-500" }, { name: tColor("blue"), value: "blue", color: "bg-blue-500" }, { name: tColor("purple"), value: "purple", color: "bg-purple-500" }, { name: tColor("pink"), value: "pink", color: "bg-pink-500" }, ]; // Filter mailboxes for move-to submenu (exclude current, drafts, virtual nodes) const moveTargets = mailboxes.filter( (m) => m.id !== selectedMailbox && m.role !== "drafts" && !m.id.startsWith("shared-") && m.myRights?.mayAddItems ); const handleAction = (action: () => void) => { action(); onClose(); }; return ( {/* Batch header */} {showBatchActions && ( {t("items_selected", { count: selectedCount })} )} {/* Single email actions - Reply, Reply All, Forward */} {!showBatchActions && ( <> handleAction(onReply!)} disabled={!onReply} /> handleAction(onReplyAll!)} disabled={!onReplyAll} /> handleAction(onForward!)} disabled={!onForward} /> )} {/* Mark as read/unread */} handleAction(() => showBatchActions ? onBatchMarkAsRead?.(isUnread) : onMarkAsRead?.(isUnread) ) } /> {/* Star/Unstar - only for single email */} {!showBatchActions && ( handleAction(onToggleStar!)} disabled={!onToggleStar} /> )} {/* Move to submenu */} {moveTargets.length > 0 && ( {moveTargets.map((mailbox) => { const Icon = getMailboxIcon(mailbox.role); return ( handleAction(() => showBatchActions ? onBatchMoveToMailbox?.(mailbox.id) : onMoveToMailbox?.(mailbox.id) ) } /> ); })} )} {/* Archive */} handleAction(onArchive!)} disabled={!onArchive} /> {/* Spam - contextual based on folder */} handleAction( showBatchActions ? (isInJunkFolder ? onBatchUndoSpam! : onBatchMarkAsSpam!) : (isInJunkFolder ? onUndoSpam! : onMarkAsSpam!) ) } disabled={showBatchActions ? (isInJunkFolder ? !onBatchUndoSpam : !onBatchMarkAsSpam) : (isInJunkFolder ? !onUndoSpam : !onMarkAsSpam)} destructive={!isInJunkFolder} /> {/* Set color submenu - only for single email */} {!showBatchActions && (
{ const buttons = Array.from( e.currentTarget.querySelectorAll("button") ); const idx = buttons.indexOf(e.target as HTMLButtonElement); if (idx < 0) return; let next = -1; if (e.key === "ArrowRight" || e.key === "ArrowDown") { next = (idx + 1) % buttons.length; } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") { next = (idx - 1 + buttons.length) % buttons.length; } if (next >= 0) { e.preventDefault(); buttons[next].focus(); } }} > {colorOptions.map((option, i) => (
{currentColor && ( <> handleAction(() => onSetColorTag?.(null))} /> )}
)} {/* Delete */} handleAction(showBatchActions ? onBatchDelete! : onDelete!) } disabled={showBatchActions ? !onBatchDelete : !onDelete} destructive />
); }