From 5b884125f7b2974f7d9314491dc30c614456e73f Mon Sep 17 00:00:00 2001 From: rustmailer Date: Wed, 22 Apr 2026 09:40:52 +0800 Subject: [PATCH] feat: nested eml quick view --- web/src/api/mailbox/envelope/api.ts | 4 +-- web/src/features/attachment/context/index.tsx | 2 +- web/src/features/attachment/index.tsx | 7 +++++ .../features/attachment/mail-list-table.tsx | 29 ++++++++++++++++--- .../attachment/nested-email-dialog.tsx | 24 ++++++++++----- web/src/features/search/mail-list-table.tsx | 2 +- .../features/search/nested-email-dialog.tsx | 6 ++-- web/src/locales/ar.json | 4 ++- web/src/locales/da.json | 4 ++- web/src/locales/de.json | 4 ++- web/src/locales/en.json | 4 ++- web/src/locales/es.json | 4 ++- web/src/locales/fi.json | 4 ++- web/src/locales/fr.json | 4 ++- web/src/locales/it.json | 4 ++- web/src/locales/jp.json | 4 ++- web/src/locales/ko.json | 4 ++- web/src/locales/nl.json | 4 ++- web/src/locales/no.json | 4 ++- web/src/locales/pl.json | 4 ++- web/src/locales/pt.json | 4 ++- web/src/locales/ru.json | 4 ++- web/src/locales/sv.json | 4 ++- web/src/locales/zh-tw.json | 4 ++- web/src/locales/zh.json | 4 ++- 25 files changed, 109 insertions(+), 37 deletions(-) diff --git a/web/src/api/mailbox/envelope/api.ts b/web/src/api/mailbox/envelope/api.ts index 1e6dd87..552e9f6 100644 --- a/web/src/api/mailbox/envelope/api.ts +++ b/web/src/api/mailbox/envelope/api.ts @@ -41,10 +41,10 @@ export const download_attachment = async (accountId: number, id: string, content saveAs(blob, fileName); }; -export const download_nested_attachment = async (accountId: number, id: string, content_hash: string, nested_content_hash: string) => { +export const download_nested_attachment = async (accountId: number, id: string, content_hash: string, nested_content_hash: string, fileName: string) => { const response = await axiosInstance.get(`api/v1/download-nested-attachment/${accountId}/${id}?content_hash=${content_hash}&nested_content_hash=${nested_content_hash}`, { responseType: 'blob' }); const blob = new Blob([response.data]); - saveAs(blob, nested_content_hash); + saveAs(blob, fileName); }; export interface AttachmentInfo { /** MIME content type of the attachment (e.g., `image/png`, `application/pdf`). */ diff --git a/web/src/features/attachment/context/index.tsx b/web/src/features/attachment/context/index.tsx index 845136a..92cb161 100644 --- a/web/src/features/attachment/context/index.tsx +++ b/web/src/features/attachment/context/index.tsx @@ -21,7 +21,7 @@ import React from 'react' import { SortingState } from '@tanstack/react-table' import { AttachmentModel } from '@/api/attachment/api' -export type AttachmentDialogType = 'mailbox' | 'display' | 'delete' | 'filters' | 'tags' | 'edit-tags' | 'update-tags' | 'restore' | 'delete-mailbox' +export type AttachmentDialogType = 'mailbox' | 'display' | 'delete' | 'filters' | 'tags' | 'edit-tags' | 'update-tags' | 'restore' | 'delete-mailbox' | 'nested-eml' interface AttachmentContextType { open: AttachmentDialogType | null diff --git a/web/src/features/attachment/index.tsx b/web/src/features/attachment/index.tsx index c0c43f6..ec76a32 100644 --- a/web/src/features/attachment/index.tsx +++ b/web/src/features/attachment/index.tsx @@ -32,6 +32,7 @@ import { AttachmentModel } from '@/api/attachment/api'; import { MailDisplayDrawer } from './mail-display-dialog'; import { EnvelopeDeleteDialog } from './delete-dialog'; import { RestoreMessageDialog } from './restore-message-dialog'; +import { NestedEmailDialog } from './nested-email-dialog'; export default function AttachmentSearch() { const { t } = useTranslation() @@ -147,6 +148,12 @@ export default function AttachmentSearch() { open={open === 'restore'} onOpenChange={() => setOpen('restore')} /> + + setOpen('nested-eml')} + /> diff --git a/web/src/features/attachment/mail-list-table.tsx b/web/src/features/attachment/mail-list-table.tsx index 97beb84..5bd9f03 100644 --- a/web/src/features/attachment/mail-list-table.tsx +++ b/web/src/features/attachment/mail-list-table.tsx @@ -165,7 +165,7 @@ export function AttachmentListTable({ accessorKey: "name", header: t('attachment.name'), cell: ({ row }) => { - const { name, content_type } = row.original; + const { name, content_type, is_message } = row.original; const safeName = name ?? "n/a"; const shortContentType = content_type @@ -179,9 +179,30 @@ export function AttachmentListTable({ contentType={content_type ?? ""} className="h-4 w-4 mt-0.5" /> - + + {is_message &&
+
+ +
+ + + +
+
} + {!is_message && {safeName} - + }
@@ -278,7 +299,7 @@ export function AttachmentListTable({ if (isLoading) { return (
- {Array.from({ length: 8 }).map((_, i) => ( + {Array.from({ length: 30 }).map((_, i) => (
diff --git a/web/src/features/attachment/nested-email-dialog.tsx b/web/src/features/attachment/nested-email-dialog.tsx index 74b5462..fe5146f 100644 --- a/web/src/features/attachment/nested-email-dialog.tsx +++ b/web/src/features/attachment/nested-email-dialog.tsx @@ -27,6 +27,7 @@ import { useQuery } from '@tanstack/react-query'; import { Download, Loader, Mail } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { getFileConfig } from './mail-message-view'; +import { useAttachmentContext } from './context'; const MessageHeader = ({ envelope, @@ -35,7 +36,7 @@ const MessageHeader = ({ }: { envelope: EmailEnvelope, attachments?: AttachmentInfo[], - onDownload: (nested_content_hash: string) => void + onDownload: (nested_content_hash: string, fileName: string) => void }) => { const { t } = useTranslation(); const displayAttachments = attachments || []; @@ -118,7 +119,7 @@ const MessageHeader = ({