diff --git a/frontend/pages/email/index.vue b/frontend/pages/email/index.vue index cca6da7..f640785 100644 --- a/frontend/pages/email/index.vue +++ b/frontend/pages/email/index.vue @@ -17,6 +17,7 @@ type EmailMailbox = { name: string delimiter?: string | null specialUse?: string | null + exists?: number unseen?: number } @@ -84,6 +85,8 @@ const expandedMailboxPaths = ref([]) const syncedMailboxPaths = ref([]) const actionLoading = ref("") const moveTargetMailboxPath = ref("") +const draggedMessageId = ref(null) +const dragOverMailboxPath = ref(null) let deepLinkApplied = false const selectedAccount = computed(() => @@ -412,8 +415,11 @@ function openComposer(mode: "reply" | "replyAll" | "forward") { function removeMessageFromCurrentList(messageId: string) { const currentIndex = messages.value.findIndex((message) => message.id === messageId) + const wasSelected = selectedMessage.value?.id === messageId messages.value = messages.value.filter((message) => message.id !== messageId) + if (!wasSelected) return + const nextMessage = messages.value[currentIndex] || messages.value[currentIndex - 1] || null selectedMessage.value = null @@ -467,17 +473,45 @@ async function deleteSelectedMessage() { async function moveSelectedMessage() { if (!selectedMessage.value || !moveTargetMailboxPath.value) return - const messageId = selectedMessage.value.id - actionLoading.value = "move" + await moveMessageToMailbox(selectedMessage.value.id, moveTargetMailboxPath.value, "move") + moveTargetMailboxPath.value = "" +} + +async function moveMessageToMailbox(messageId: string, mailboxPath: string, loadingKey = `move-${messageId}`) { + const message = messages.value.find((item) => item.id === messageId) + if (!message || message.mailboxPath === mailboxPath) return + + actionLoading.value = loadingKey try { await $api(`/api/email/messages/${messageId}/move`, { method: "POST", - body: { mailbox: moveTargetMailboxPath.value }, + body: { mailbox: mailboxPath }, + }) + mailboxes.value = mailboxes.value.map((mailbox) => { + if (mailbox.path === message.mailboxPath) { + return { + ...mailbox, + exists: Math.max(0, Number(mailbox.exists || 0) - 1), + unseen: Math.max(0, Number(mailbox.unseen || 0) - (message.seen ? 0 : 1)), + } + } + if (mailbox.path === mailboxPath) { + return { + ...mailbox, + exists: Number(mailbox.exists || 0) + 1, + unseen: Number(mailbox.unseen || 0) + (message.seen ? 0 : 1), + } + } + return mailbox }) removeMessageFromCurrentList(messageId) - toast.add({ title: "E-Mail verschoben", color: "success" }) - moveTargetMailboxPath.value = "" + const targetMailbox = mailboxes.value.find((mailbox) => mailbox.path === mailboxPath) + toast.add({ + title: "E-Mail verschoben", + description: targetMailbox ? `Abgelegt in „${mailboxLabel(targetMailbox)}“` : undefined, + color: "success", + }) } catch (err: any) { toast.add({ title: "Verschieben fehlgeschlagen", @@ -489,6 +523,53 @@ async function moveSelectedMessage() { } } +function startMessageDrag(event: DragEvent, message: EmailMessage) { + if (!event.dataTransfer || actionLoading.value) { + event.preventDefault() + return + } + + draggedMessageId.value = message.id + event.dataTransfer.effectAllowed = "move" + event.dataTransfer.setData("application/x-fedeo-email-id", message.id) + event.dataTransfer.setData("text/plain", message.id) +} + +function finishMessageDrag() { + draggedMessageId.value = null + dragOverMailboxPath.value = null +} + +function dragMessageOverMailbox(event: DragEvent, mailbox: EmailMailbox) { + const message = messages.value.find((item) => item.id === draggedMessageId.value) + if (!message || message.mailboxPath === mailbox.path) { + if (event.dataTransfer) event.dataTransfer.dropEffect = "none" + return + } + + event.preventDefault() + dragOverMailboxPath.value = mailbox.path + if (event.dataTransfer) event.dataTransfer.dropEffect = "move" +} + +function leaveMailboxDropTarget(event: DragEvent, mailbox: EmailMailbox) { + const currentTarget = event.currentTarget as HTMLElement | null + const relatedTarget = event.relatedTarget as Node | null + if (currentTarget && relatedTarget && currentTarget.contains(relatedTarget)) return + if (dragOverMailboxPath.value === mailbox.path) dragOverMailboxPath.value = null +} + +async function dropMessageOnMailbox(event: DragEvent, mailbox: EmailMailbox) { + event.preventDefault() + const messageId = draggedMessageId.value + || event.dataTransfer?.getData("application/x-fedeo-email-id") + || "" + + finishMessageDrag() + if (!messageId) return + await moveMessageToMailbox(messageId, mailbox.path) +} + async function downloadAttachment(attachment: NonNullable[number]) { actionLoading.value = `attachment-${attachment.id}` try { @@ -682,13 +763,27 @@ onMounted(loadAccounts)