From f2adc21fea7ed37a6c8b585117b15932f921fd1b Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 23 May 2026 20:15:43 +0200 Subject: [PATCH] E-Mail Ordner einklappbar machen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KI-AGENT: Ergänzt auf- und zuklappbare IMAP-Ordner in der E-Mail Übersicht. Elternordner bleiben initial sichtbar, ausgewählte Unterordner öffnen automatisch ihre übergeordneten Ordner. --- frontend/pages/email/index.vue | 63 +++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/frontend/pages/email/index.vue b/frontend/pages/email/index.vue index ebe4f9f..0046dad 100644 --- a/frontend/pages/email/index.vue +++ b/frontend/pages/email/index.vue @@ -69,6 +69,7 @@ const loadingMailboxes = ref(false) const loadingMessages = ref(false) const loadingMessage = ref(false) const syncing = ref(false) +const expandedMailboxPaths = ref([]) const selectedAccount = computed(() => accounts.value.find((account) => account.id === selectedAccountId.value) || null @@ -144,10 +145,13 @@ const mailboxTree = computed(() => { }) const mailboxRows = computed(() => { - const rows: Array<{ mailbox: EmailMailbox; depth: number }> = [] + const rows: Array<{ mailbox: EmailMailbox; depth: number; hasChildren: boolean }> = [] const append = (node: EmailMailboxNode, depth: number) => { - rows.push({ mailbox: node.mailbox, depth }) - node.children.forEach((child) => append(child, depth + 1)) + const expanded = expandedMailboxPaths.value.includes(node.mailbox.path) + rows.push({ mailbox: node.mailbox, depth, hasChildren: node.children.length > 0 }) + if (expanded) { + node.children.forEach((child) => append(child, depth + 1)) + } } mailboxTree.value.forEach((node) => append(node, 0)) @@ -186,6 +190,47 @@ const mailboxLabel = (mailbox: EmailMailbox) => { return mailbox.name || mailbox.path } +const isMailboxExpanded = (mailbox: EmailMailbox) => expandedMailboxPaths.value.includes(mailbox.path) + +const expandMailboxPath = (path: string) => { + if (!expandedMailboxPaths.value.includes(path)) { + expandedMailboxPaths.value = [...expandedMailboxPaths.value, path] + } +} + +const collapseMailboxPath = (path: string) => { + expandedMailboxPaths.value = expandedMailboxPaths.value.filter((item) => item !== path) +} + +const toggleMailboxExpanded = (mailbox: EmailMailbox) => { + if (isMailboxExpanded(mailbox)) { + collapseMailboxPath(mailbox.path) + } else { + expandMailboxPath(mailbox.path) + } +} + +const expandMailboxAncestors = (path: string) => { + const mailbox = mailboxes.value.find((item) => item.path === path) + if (!mailbox) return + + const delimiter = mailboxDelimiter(mailbox) + const parts = path.split(delimiter) + + while (parts.length > 1) { + parts.pop() + expandMailboxPath(parts.join(delimiter)) + } +} + +const resetExpandedMailboxes = () => { + const roots = mailboxTree.value.map((node) => node.mailbox.path) + expandedMailboxPaths.value = Array.from(new Set([ + ...roots, + ...expandedMailboxPaths.value.filter((path) => mailboxes.value.some((mailbox) => mailbox.path === path)), + ])) +} + const formatAddress = (address?: EmailAddress | null) => { if (!address) return "Unbekannt" return address.name || address.address || "Unbekannt" @@ -254,8 +299,10 @@ async function loadMailboxes() { try { mailboxes.value = await $api(`/api/email/accounts/${selectedAccountId.value}/mailboxes`) + resetExpandedMailboxes() const inbox = mailboxes.value.find((mailbox) => mailbox.specialUse === "\\Inbox" || mailbox.path.toUpperCase() === "INBOX") selectedMailboxPath.value = inbox?.path || mailboxes.value[0]?.path || "INBOX" + expandMailboxAncestors(selectedMailboxPath.value) await loadMessages() } finally { loadingMailboxes.value = false @@ -286,6 +333,7 @@ async function loadMessages() { async function selectMailbox(mailbox: EmailMailbox) { selectedMailboxPath.value = mailbox.path + expandMailboxAncestors(mailbox.path) await loadMessages() } @@ -467,9 +515,14 @@ onMounted(loadAccounts) @click="selectMailbox(row.mailbox)" > + {{ mailboxLabel(row.mailbox) }}