diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx index 4d76c768..f593f4ea 100644 --- a/app/[locale]/page.tsx +++ b/app/[locale]/page.tsx @@ -468,6 +468,33 @@ export default function Home() { if (isMobile) setActiveView('viewer'); }; + const handleEditDraft = (email?: Email) => { + const draft = email || selectedEmail; + if (!draft) return; + const bodyText = draft.bodyValues + ? Object.values(draft.bodyValues).map(v => v.value).join('\n') + : ''; + const htmlBody = draft.htmlBody?.[0]?.partId && draft.bodyValues?.[draft.htmlBody[0].partId] + ? draft.bodyValues[draft.htmlBody[0].partId].value + : undefined; + setPendingDraft({ + to: draft.to?.map(a => a.email).filter(Boolean).join(', ') || '', + cc: draft.cc?.map(a => a.email).filter(Boolean).join(', ') || '', + bcc: draft.bcc?.map(a => a.email).filter(Boolean).join(', ') || '', + subject: draft.subject || '', + body: htmlBody || bodyText, + showCc: (draft.cc?.length || 0) > 0, + showBcc: (draft.bcc?.length || 0) > 0, + selectedIdentityId: null, + subAddressTag: '', + mode: 'compose', + draftId: draft.id, + }); + setComposerMode('compose'); + setShowComposer(true); + if (isMobile) setActiveView('viewer'); + }; + const handleReplyAll = () => { setComposerMode('replyAll'); setShowComposer(true); @@ -1370,6 +1397,9 @@ export default function Home() { selectEmail(email); await handleUndoSpam(); }} + onEditDraft={(email) => { + handleEditDraft(email); + }} className="flex-1 min-h-0" /> @@ -1543,6 +1573,7 @@ export default function Home() { onNavigateNext={handleNavigateNext} onNavigatePrev={handleNavigatePrev} onShowShortcuts={() => setShowShortcutsModal(true)} + onEditDraft={handleEditDraft} currentUserEmail={client?.["username"]} currentUserName={client?.["username"]?.split("@")[0]} currentMailboxRole={mailboxes.find(m => m.id === selectedMailbox)?.role} diff --git a/components/email/email-context-menu.tsx b/components/email/email-context-menu.tsx index ecf449f2..34c99575 100644 --- a/components/email/email-context-menu.tsx +++ b/components/email/email-context-menu.tsx @@ -28,6 +28,7 @@ import { Folder, ShieldAlert, ShieldCheck, + EditIcon, } from "lucide-react"; import { cn, buildMailboxTree, MailboxNode } from "@/lib/utils"; import { useSettingsStore, KEYWORD_PALETTE } from "@/stores/settings-store"; @@ -60,6 +61,7 @@ interface EmailContextMenuProps { onMoveToMailbox?: (mailboxId: string) => void; onMarkAsSpam?: () => void; onUndoSpam?: () => void; + onEditDraft?: () => void; // Batch actions onBatchMarkAsRead?: (read: boolean) => void; onBatchDelete?: () => void; @@ -126,12 +128,14 @@ export function EmailContextMenu({ onBatchMoveToMailbox, onBatchMarkAsSpam, onBatchUndoSpam, + onEditDraft, }: EmailContextMenuProps) { const t = useTranslations("context_menu"); const tColor = useTranslations("email_viewer.color_tag"); const emailKeywords = useSettingsStore((state) => state.emailKeywords); const isUnread = !email.keywords?.$seen; const isStarred = email.keywords?.$flagged; + const isDraft = email.keywords?.['$draft'] === true; const currentColor = getCurrentColor(email.keywords); const showBatchActions = isMultiSelect && selectedCount > 1; const isInJunkFolder = currentMailboxRole === 'junk'; @@ -188,6 +192,18 @@ export function EmailContextMenu({ )} + {/* Edit Draft - only for single draft emails */} + {!showBatchActions && isDraft && onEditDraft && ( + <> + handleAction(onEditDraft)} + /> + + + )} + {/* Single email actions - Reply, Reply All, Forward */} {!showBatchActions && ( <> diff --git a/components/email/email-list.tsx b/components/email/email-list.tsx index 5c02d802..095913f2 100644 --- a/components/email/email-list.tsx +++ b/components/email/email-list.tsx @@ -37,6 +37,7 @@ interface EmailListProps { onMoveToMailbox?: (emailId: string, mailboxId: string) => void; onMarkAsSpam?: (email: Email) => void; onUndoSpam?: (email: Email) => void; + onEditDraft?: (email: Email) => void; } export function EmailList({ @@ -57,6 +58,7 @@ export function EmailList({ onMarkAsSpam, onUndoSpam, onMoveToMailbox, + onEditDraft, }: EmailListProps) { const t = useTranslations('email_list'); const { client } = useAuthStore(); @@ -467,6 +469,7 @@ export function EmailList({ onMoveToMailbox={(mailboxId) => onMoveToMailbox?.(contextMenu.data!.id, mailboxId)} onMarkAsSpam={() => onMarkAsSpam?.(contextMenu.data!)} onUndoSpam={() => onUndoSpam?.(contextMenu.data!)} + onEditDraft={() => onEditDraft?.(contextMenu.data!)} onBatchMarkAsRead={(read) => client && batchMarkAsRead(client, read)} onBatchDelete={() => client && batchDelete(client)} onBatchMoveToMailbox={(mailboxId) => client && batchMoveToMailbox(client, mailboxId)} diff --git a/components/email/email-viewer.tsx b/components/email/email-viewer.tsx index 6b93c485..c3770397 100644 --- a/components/email/email-viewer.tsx +++ b/components/email/email-viewer.tsx @@ -64,6 +64,7 @@ import { Upload, Moon, HelpCircle, + EditIcon, } from "lucide-react"; import { useTranslations } from "next-intl"; import type { Attachment as PostalMimeAttachment } from 'postal-mime'; @@ -112,6 +113,7 @@ interface EmailViewerProps { onNavigateNext?: () => void; onNavigatePrev?: () => void; onShowShortcuts?: () => void; + onEditDraft?: () => void; currentUserEmail?: string; currentUserName?: string; currentMailboxRole?: string; @@ -815,6 +817,7 @@ export function EmailViewer({ onNavigateNext, onNavigatePrev, onShowShortcuts, + onEditDraft, currentUserEmail, currentUserName, currentMailboxRole, @@ -840,6 +843,9 @@ export function EmailViewer({ // Detect if current mailbox is Junk folder const isInJunkFolder = currentMailboxRole === 'junk'; + // Detect if the email is a draft + const isDraft = email?.keywords?.['$draft'] === true; + // Color options for email tags (from user-defined keyword settings) const colorOptions = emailKeywords.map((kw) => ({ name: kw.label, @@ -2632,6 +2638,19 @@ export function EmailViewer({ )} + {isDraft && onEditDraft && ( + + )} + {!isDraft && (<> + )} {/* Right: Organize actions — order: archive, delete, move, star, tag, spam, read state, print, view source */} @@ -3988,6 +4008,29 @@ export function EmailViewer({ )} + {/* Draft Banner */} + {isDraft && ( +
+
+
+ + {t('draft_banner')} +
+ {onEditDraft && ( + + )} +
+
+ )} + { @@ -4077,8 +4120,8 @@ export function EmailViewer({ )} - {/* Quick Reply Section */} - - + )} @@ -4240,6 +4283,17 @@ export function EmailViewer({ {t('previous')} + {isDraft && onEditDraft ? ( + + ) : ( + <> + )}