mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
feat: nested eml quick view
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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')}
|
||||
/>
|
||||
|
||||
<NestedEmailDialog
|
||||
key="nested-eml-attachment-dialog"
|
||||
open={open === 'nested-eml'}
|
||||
onOpenChange={() => setOpen('nested-eml')}
|
||||
/>
|
||||
</AttachmentProvider>
|
||||
</Main>
|
||||
</>
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
<LongText className='text-xs font-medium max-w-[320px] text-foreground/90'>
|
||||
|
||||
{is_message && <div className="group relative flex items-center w-full min-w-0 h-full px-2 overflow-hidden">
|
||||
<div className="absolute left-0 top-0 bottom-0 w-[2px] bg-primary opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
|
||||
<div className="text-xs flex flex-wrap gap-x-1 min-w-0 flex-1">
|
||||
<span className="flex items-center">
|
||||
<button
|
||||
type="button"
|
||||
title={t('attachment.viewEmbeddedEmail')}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setCurrentAttachment(row.original);
|
||||
setOpen("nested-eml");
|
||||
}}
|
||||
className="hover:text-primary hover:underline transition-colors truncate"
|
||||
>
|
||||
<LongText>{safeName}</LongText>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>}
|
||||
{!is_message && <LongText className='text-xs font-medium max-w-[320px] text-foreground/90'>
|
||||
{safeName}
|
||||
</LongText>
|
||||
</LongText>}
|
||||
</div>
|
||||
<div className="flex items-center gap-1 ml-6.5 mt-1">
|
||||
<span className="text-[10px] text-muted-foreground font-mono bg-muted px-1 py-0.5 rounded-sm">
|
||||
@@ -278,7 +299,7 @@ export function AttachmentListTable({
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="divide-y divide-border">
|
||||
{Array.from({ length: 8 }).map((_, i) => (
|
||||
{Array.from({ length: 30 }).map((_, i) => (
|
||||
<div key={i} className="flex items-center gap-2 px-2 py-1.5">
|
||||
<Skeleton className="h-3 w-3" />
|
||||
<Skeleton className="h-3 w-3 rounded-full" />
|
||||
|
||||
@@ -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 = ({
|
||||
<Tooltip key={i}>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
onClick={() => onDownload(att.content_hash)}
|
||||
onClick={() => onDownload(att.content_hash, att.filename)}
|
||||
className="group flex items-center gap-2 px-3 py-1.5 bg-slate-50 border border-slate-200 rounded-lg hover:bg-blue-50 hover:border-blue-200 transition-all text-slate-600 hover:text-blue-700"
|
||||
>
|
||||
<span className={`${color} p-0.5 rounded`}>{icon}</span>
|
||||
@@ -144,12 +145,13 @@ const MessageHeader = ({
|
||||
|
||||
|
||||
|
||||
export function NestedEmailDialog({ open, onOpenChange, accountId, envelopeId, fileName, content_hash }: any) {
|
||||
export function NestedEmailDialog({ open, onOpenChange }: any) {
|
||||
const { currentAttachment } = useAttachmentContext()
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['nested-message', accountId, envelopeId, content_hash],
|
||||
queryFn: () => load_nested_message(accountId, envelopeId, content_hash),
|
||||
enabled: open && !!content_hash,
|
||||
queryKey: ['nested-message', currentAttachment?.account_id!, currentAttachment?.envelope_id!, currentAttachment?.content_hash!],
|
||||
queryFn: () => load_nested_message(currentAttachment?.account_id!, currentAttachment?.envelope_id!, currentAttachment?.content_hash!),
|
||||
enabled: open && !!currentAttachment,
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -158,7 +160,7 @@ export function NestedEmailDialog({ open, onOpenChange, accountId, envelopeId, f
|
||||
<div className="text-white px-4 py-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Mail className="h-4 w-4 text-blue-400" />
|
||||
<span className="text-sm font-medium truncate max-w-[400px] opacity-90">{fileName}</span>
|
||||
<span className="text-sm font-medium truncate max-w-[400px] opacity-90">{currentAttachment?.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -170,7 +172,13 @@ export function NestedEmailDialog({ open, onOpenChange, accountId, envelopeId, f
|
||||
<MessageHeader
|
||||
envelope={data.envelope}
|
||||
attachments={data.attachments}
|
||||
onDownload={(nested_content_hash) => download_nested_attachment(accountId, envelopeId, content_hash, nested_content_hash)}
|
||||
onDownload={(nested_content_hash, fileName) => download_nested_attachment(
|
||||
currentAttachment?.account_id!,
|
||||
currentAttachment?.envelope_id!,
|
||||
currentAttachment?.content_hash!,
|
||||
nested_content_hash,
|
||||
fileName
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="mt-8 pt-8 border-t border-slate-100">
|
||||
|
||||
Reference in New Issue
Block a user