diff --git a/app/api/dev-jmap/[...path]/route.ts b/app/api/dev-jmap/[...path]/route.ts index e5cc03c2..61586a84 100644 --- a/app/api/dev-jmap/[...path]/route.ts +++ b/app/api/dev-jmap/[...path]/route.ts @@ -37,6 +37,8 @@ interface MockMailbox { sortOrder: number; totalEmails: number; unreadEmails: number; + parentId?: string; + isSubscribed: boolean; } interface MockEmail { @@ -64,12 +66,15 @@ function nextState(): string { } const mailboxes: MockMailbox[] = [ - { id: 'mb-inbox', name: 'Inbox', role: 'inbox', sortOrder: 1, totalEmails: 5, unreadEmails: 2 }, - { id: 'mb-drafts', name: 'Drafts', role: 'drafts', sortOrder: 2, totalEmails: 1, unreadEmails: 0 }, - { id: 'mb-sent', name: 'Sent', role: 'sent', sortOrder: 3, totalEmails: 3, unreadEmails: 0 }, - { id: 'mb-junk', name: 'Junk', role: 'junk', sortOrder: 4, totalEmails: 1, unreadEmails: 1 }, - { id: 'mb-trash', name: 'Trash', role: 'trash', sortOrder: 5, totalEmails: 0, unreadEmails: 0 }, - { id: 'mb-archive', name: 'Archive', role: 'archive', sortOrder: 6, totalEmails: 2, unreadEmails: 0 }, + { id: 'mb-inbox', name: 'Inbox', role: 'inbox', sortOrder: 1, totalEmails: 5, unreadEmails: 2, isSubscribed: true }, + { id: 'mb-drafts', name: 'Drafts', role: 'drafts', sortOrder: 2, totalEmails: 1, unreadEmails: 0, isSubscribed: true }, + { id: 'mb-sent', name: 'Sent', role: 'sent', sortOrder: 3, totalEmails: 3, unreadEmails: 0, isSubscribed: true }, + { id: 'mb-junk', name: 'Junk', role: 'junk', sortOrder: 4, totalEmails: 1, unreadEmails: 1, isSubscribed: true }, + { id: 'mb-trash', name: 'Trash', role: 'trash', sortOrder: 5, totalEmails: 0, unreadEmails: 0, isSubscribed: true }, + { id: 'mb-archive', name: 'Archive', role: 'archive', sortOrder: 6, totalEmails: 2, unreadEmails: 0, isSubscribed: true }, + { id: 'mb-invoices', name: 'Invoices', role: null, sortOrder: 7, totalEmails: 8, unreadEmails: 0, isSubscribed: true }, + { id: 'mb-invoices-2025', name: '2025', role: null, sortOrder: 8, totalEmails: 8, unreadEmails: 0, parentId: "mb-invoices", isSubscribed: false }, + { id: 'mb-invoices-2026', name: '2026', role: null, sortOrder: 9, totalEmails: 8, unreadEmails: 0, parentId: "mb-invoices", isSubscribed: true }, ]; function recomputeMailboxCounts(): void { @@ -1544,6 +1549,8 @@ function handleMailboxSet(args: MethodArgs, callId: string): MethodResult { sortOrder: mailboxes.length + 1, totalEmails: 0, unreadEmails: 0, + parentId: typeof data.parentId === 'string' ? (data.parentId as string) : undefined, + isSubscribed: data.isSubscribed !== false, }); created[key] = { id: newId }; } @@ -1556,6 +1563,7 @@ function handleMailboxSet(args: MethodArgs, callId: string): MethodResult { if (mb) { if (changes.name !== undefined) mb.name = changes.name as string; if (changes.sortOrder !== undefined) mb.sortOrder = changes.sortOrder as number; + if (changes.isSubscribed !== undefined) mb.isSubscribed = changes.isSubscribed as boolean; updated[id] = null; } } diff --git a/components/email/email-context-menu.tsx b/components/email/email-context-menu.tsx index 3f128b7b..e2d2b7cf 100644 --- a/components/email/email-context-menu.tsx +++ b/components/email/email-context-menu.tsx @@ -172,7 +172,7 @@ export function EmailContextMenu({ const filterTree = (nodes: MailboxNode[]): MailboxNode[] => { return nodes.reduce((acc, node) => { const filteredChildren = filterTree(node.children); - if (moveTargetIds.has(node.id) || filteredChildren.length > 0) { + if (node.isSubscribed && (moveTargetIds.has(node.id) || filteredChildren.length > 0)) { acc.push({ ...node, children: filteredChildren }); } return acc; diff --git a/components/layout/sidebar.tsx b/components/layout/sidebar.tsx index 3700a5d5..dd81d947 100644 --- a/components/layout/sidebar.tsx +++ b/components/layout/sidebar.tsx @@ -37,7 +37,7 @@ import { MailOpen, MoreHorizontal, } from "lucide-react"; -import { cn, buildMailboxTree, MailboxNode } from "@/lib/utils"; +import { cn, buildMailboxTree, hasSubscribedChildren, MailboxNode } from "@/lib/utils"; import { localizeMailboxName } from "@/lib/mailbox-label"; import { buildKeywordTree, @@ -495,7 +495,7 @@ function MailboxTreeItem({ }) { const tNotifications = useTranslations('notifications'); const tSidebar = useTranslations('sidebar'); - const hasChildren = node.children.length > 0; + const hasChildren = node.isSubscribed && hasSubscribedChildren(node); const isExpanded = expandedFolders.has(node.id); const Icon = getIconForMailbox(node.role, node.name, hasChildren, isExpanded, node.isShared, node.id); const isVirtualNode = node.id.startsWith('shared-'); @@ -544,7 +544,7 @@ function MailboxTreeItem({ return ( <> - } label={label} testRole={node.role} @@ -573,7 +573,7 @@ function MailboxTreeItem({ isValidDropTarget={isValidDropTarget} isInvalidDropTarget={isInvalidDropTarget} onContextMenu={onContextMenu && !isVirtualNode ? (e) => onContextMenu(e, node) : undefined} - /> + />)} {hasChildren && isExpanded && !isCollapsed && node.children.map((child) => ( { + if (!client) return; + setIsLoading(true); + try { + await setMailboxSubscription(client, mailboxId, subscribe); + } catch (error) { + console.error('Failed to update folder subscription:', error); + toast.error('Failed to update folder subscription'); + } finally { + setIsLoading(false); + } + }; + const cancelEdit = () => { setEditingId(null); setEditingName(''); @@ -577,7 +591,7 @@ export function FolderSettings() { /> )} - {mb.name} + {mb.name} {mb.role && ( {t(`role_${mb.role}`)} @@ -590,6 +604,15 @@ export function FolderSettings() { )}
+ {mb.role == null && + + } {mb.myRights?.mayCreateChild && (