inbox streamlining
This commit is contained in:
parent
f969cfed44
commit
220168abd0
4 changed files with 279 additions and 245 deletions
|
|
@ -77,18 +77,37 @@ function metricTotal(stats) {
|
|||
}
|
||||
|
||||
function emailContextSummary(email) {
|
||||
const text = (email?.body_text || email?.snippet || "").trim();
|
||||
if (!text) return "Open message loaded — summary will appear here.";
|
||||
return text.length > 160 ? `${text.slice(0, 157)}…` : text;
|
||||
const text = (email?.body_text || email?.snippet || "").trim().toLowerCase();
|
||||
const sender = email?.sender?.name || email?.sender_display_name || "The sender";
|
||||
if (!text) return "This message is open — a summary will appear here.";
|
||||
if (text.includes("check") && text.includes("inbox") && text.includes("repl")) {
|
||||
return `${sender} asks you to check the inbox and review messages and replies.`;
|
||||
}
|
||||
if (text.includes("check") && text.includes("inbox")) {
|
||||
return `${sender} asks you to check the inbox.`;
|
||||
}
|
||||
if (text.includes("repl")) {
|
||||
return `${sender} is following up about replies or responses.`;
|
||||
}
|
||||
if (text.includes("attach")) {
|
||||
return `${sender} references an attachment that may need review.`;
|
||||
}
|
||||
const snippet = (email?.body_text || email?.snippet || "").trim();
|
||||
const firstSentence = snippet.split(/[.!?\n]/).find((part) => part.trim().length > 8)?.trim();
|
||||
if (firstSentence) {
|
||||
return `${sender} wrote: “${firstSentence.length > 120 ? `${firstSentence.slice(0, 117)}…` : firstSentence}.”`;
|
||||
}
|
||||
return `${sender} sent a short message that may need a response.`;
|
||||
}
|
||||
|
||||
function emailContextActions(email) {
|
||||
const text = (email?.body_text || email?.snippet || "").toLowerCase();
|
||||
const actions = [];
|
||||
if (text.includes("check") && text.includes("inbox")) actions.push("Check inbox");
|
||||
if (text.includes("message")) actions.push("Review messages");
|
||||
if (text.includes("repl")) actions.push("Review replies");
|
||||
if (text.includes("attach")) actions.push("Review attachments");
|
||||
if (!actions.length) actions.push("Review message", "Draft a reply");
|
||||
if (!actions.length) actions.push("Review message");
|
||||
return actions.slice(0, 3);
|
||||
}
|
||||
|
||||
|
|
@ -170,9 +189,12 @@ export default function AgAgentPanel({
|
|||
<h3>Summary</h3>
|
||||
<p>{emailContextSummary(contextEmail)}</p>
|
||||
<h3>Action items</h3>
|
||||
<ul>
|
||||
<ul className="agent-email-action-list">
|
||||
{emailContextActions(contextEmail).map((item) => (
|
||||
<li key={item}>{item}</li>
|
||||
<li key={item}>
|
||||
<span className="agent-email-action-check" aria-hidden="true" />
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<button type="button" className="agent-email-context-reply" onClick={() => onCompose?.()}>
|
||||
|
|
@ -209,7 +231,7 @@ export default function AgAgentPanel({
|
|||
</div>
|
||||
)}
|
||||
|
||||
{showFullPanel && !allMetricsZero && (
|
||||
{showFullPanel && !emailContextMode && !allMetricsZero && (
|
||||
<div className="agent-stat-grid">
|
||||
{cards.map((card) => {
|
||||
const Icon = card.icon;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { useState } from "react";
|
||||
import {
|
||||
Archive,
|
||||
ArrowLeft,
|
||||
|
|
@ -20,7 +21,7 @@ import SenderAvatar from "./SenderAvatar";
|
|||
import { addressText, senderDisplayName } from "../utils/senderAvatar";
|
||||
import { attachmentCount } from "../utils/inboxSignals";
|
||||
|
||||
const ICON = { size: 17, strokeWidth: 1.7 };
|
||||
const ICON = { size: 16, strokeWidth: 1.7 };
|
||||
|
||||
function joinAddresses(value) {
|
||||
if (!value) return "";
|
||||
|
|
@ -45,13 +46,6 @@ function messageBody(email) {
|
|||
return { mode: "text", value: "This message has no readable content." };
|
||||
}
|
||||
|
||||
function plainTextParagraphs(text) {
|
||||
return text
|
||||
.split(/\n{2,}/)
|
||||
.map((block) => block.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function detailTime(email) {
|
||||
const value = email?.received_at || email?.sent_at || email?.created_at || email?.updated_at;
|
||||
if (!value) return { absolute: "", relative: "" };
|
||||
|
|
@ -73,9 +67,9 @@ function senderIdentity(email) {
|
|||
const name = senderDisplayName(email);
|
||||
const address = addressText(email.sender || email.from);
|
||||
if (name && address && !name.includes("@")) {
|
||||
return `${name} <${address}>`;
|
||||
return { name, address, display: `${name} <${address}>` };
|
||||
}
|
||||
return address || name || "Unknown sender";
|
||||
return { name: name || address, address, display: address || name || "Unknown sender" };
|
||||
}
|
||||
|
||||
function recipientLabel(email, viewerEmail) {
|
||||
|
|
@ -106,11 +100,17 @@ function mailboxLabel(mailbox) {
|
|||
return mailbox;
|
||||
}
|
||||
|
||||
function ToolbarButton({ title, label, onClick, disabled, danger, children }) {
|
||||
function hasMultipleRecipients(email) {
|
||||
const toCount = Array.isArray(email.recipients || email.to) ? (email.recipients || email.to).length : 0;
|
||||
const ccCount = Array.isArray(email.cc) ? email.cc.length : 0;
|
||||
return toCount > 1 || ccCount > 0;
|
||||
}
|
||||
|
||||
function ToolbarButton({ title, label, onClick, disabled, destructive, children }) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={danger ? "danger" : ""}
|
||||
className={destructive ? "destructive" : ""}
|
||||
title={title}
|
||||
aria-label={label || title}
|
||||
onClick={onClick}
|
||||
|
|
@ -144,201 +144,205 @@ export default function MailDetail({
|
|||
onExtractActions,
|
||||
onSummarizeThread,
|
||||
onAnalyzeAttachment,
|
||||
onDraftReply,
|
||||
}) {
|
||||
const [headersOpen, setHeadersOpen] = useState(false);
|
||||
const files = email ? attachmentCount(email) : 0;
|
||||
const body = email ? messageBody(email) : null;
|
||||
const time = email ? detailTime(email) : { absolute: "", relative: "" };
|
||||
const attachments = email?.attachments || [];
|
||||
const summarizeLabel = threadSize > 1 ? "Summarize thread" : "Summarize email";
|
||||
const sender = email ? senderIdentity(email) : null;
|
||||
const summarizeLabel = threadSize > 1 ? "Summarize thread" : "Summarize";
|
||||
const showReplyAll = email ? hasMultipleRecipients(email) : false;
|
||||
|
||||
return (
|
||||
<article className={`mail-detail${email || loading ? " open" : ""}`} aria-live="polite">
|
||||
<header className="mail-detail-toolbar">
|
||||
<div className="mail-detail-toolbar-group">
|
||||
<ToolbarButton title="Back to inbox" label="Back" onClick={onClose}>
|
||||
<ArrowLeft {...ICON} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
{email && (
|
||||
<>
|
||||
<div className="mail-detail-toolbar-group">
|
||||
<ToolbarButton title="Archive" onClick={onArchive} disabled={busy}>
|
||||
<Archive {...ICON} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton title="Report spam" onClick={onSpam} disabled={busy}>
|
||||
<ShieldAlert {...ICON} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
title={mailbox === "trash" ? "Delete permanently" : "Delete"}
|
||||
onClick={mailbox === "trash" ? onDelete : onTrash}
|
||||
disabled={busy}
|
||||
danger
|
||||
>
|
||||
<Trash2 {...ICON} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
<span className="mail-detail-toolbar-sep" aria-hidden="true" />
|
||||
<div className="mail-detail-toolbar-group">
|
||||
<ToolbarButton
|
||||
title={email.is_read ? "Mark unread" : "Mark read"}
|
||||
onClick={() => onToggleRead(!email.is_read)}
|
||||
disabled={busy}
|
||||
>
|
||||
{email.is_read ? <Mail {...ICON} /> : <MailOpen {...ICON} />}
|
||||
</ToolbarButton>
|
||||
<ToolbarButton title="Snooze" onClick={onSnooze} disabled={busy}>
|
||||
<Clock {...ICON} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
<span className="mail-detail-toolbar-sep" aria-hidden="true" />
|
||||
<div className="mail-detail-toolbar-group">
|
||||
<ToolbarButton title="Move to" onClick={onMove} disabled={busy}>
|
||||
<FolderInput {...ICON} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton title="Labels" onClick={onMove} disabled={busy}>
|
||||
<Tag {...ICON} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton title="More actions" onClick={onMove} disabled={busy}>
|
||||
<MoreHorizontal {...ICON} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
{mailbox === "trash" && (
|
||||
<>
|
||||
<span className="mail-detail-toolbar-sep" aria-hidden="true" />
|
||||
<div className="mail-detail-toolbar-group">
|
||||
<ToolbarButton title="Restore" onClick={onRestore} disabled={busy}>
|
||||
<RotateCcw {...ICON} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</header>
|
||||
|
||||
{loading && <div className="mail-detail-state">Opening message…</div>}
|
||||
{error && <div className="mail-detail-state error" role="alert">{error}</div>}
|
||||
|
||||
{email && !loading && (
|
||||
<div className="mail-detail-content">
|
||||
<div className="mail-detail-subject-row">
|
||||
<div className="mail-detail-subject-main">
|
||||
<h1>{email.subject?.trim() || "(No subject)"}</h1>
|
||||
<div className="mail-detail-labels">
|
||||
<span className="mail-detail-label-pill">{mailboxLabel(mailbox)}</span>
|
||||
{email.is_starred && <span className="mail-detail-label-pill starred">Starred</span>}
|
||||
<div className="mail-detail-reader">
|
||||
<header className="mail-detail-toolbar">
|
||||
<div className="mail-detail-toolbar-group">
|
||||
<ToolbarButton title="Back to inbox" label="Back" onClick={onClose}>
|
||||
<ArrowLeft size={16} strokeWidth={1.7} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
{email && (
|
||||
<>
|
||||
<div className="mail-detail-toolbar-group">
|
||||
<ToolbarButton title="Archive" onClick={onArchive} disabled={busy}>
|
||||
<Archive {...ICON} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton title="Report spam" onClick={onSpam} disabled={busy}>
|
||||
<ShieldAlert {...ICON} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
title={mailbox === "trash" ? "Delete permanently" : "Delete"}
|
||||
onClick={mailbox === "trash" ? onDelete : onTrash}
|
||||
disabled={busy}
|
||||
destructive
|
||||
>
|
||||
<Trash2 {...ICON} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mail-detail-ai-chips">
|
||||
<button type="button" className="mail-detail-ai-chip" onClick={onSummarizeThread}>
|
||||
<AgentSpark /> {summarizeLabel}
|
||||
</button>
|
||||
<button type="button" className="mail-detail-ai-chip" onClick={onExtractActions}>
|
||||
<AgentSpark /> Extract actions
|
||||
</button>
|
||||
<button type="button" className="mail-detail-ai-chip" onClick={onDraftReply}>
|
||||
<AgentSpark /> Draft reply
|
||||
</button>
|
||||
{files > 0 && (
|
||||
<button type="button" className="mail-detail-ai-chip" onClick={onAnalyzeAttachment}>
|
||||
<AgentSpark /> Analyze attachment
|
||||
</button>
|
||||
<span className="mail-detail-toolbar-sep" aria-hidden="true" />
|
||||
<div className="mail-detail-toolbar-group">
|
||||
<ToolbarButton
|
||||
title={email.is_read ? "Mark unread" : "Mark read"}
|
||||
onClick={() => onToggleRead(!email.is_read)}
|
||||
disabled={busy}
|
||||
>
|
||||
{email.is_read ? <Mail {...ICON} /> : <MailOpen {...ICON} />}
|
||||
</ToolbarButton>
|
||||
<ToolbarButton title="Snooze" onClick={onSnooze} disabled={busy}>
|
||||
<Clock {...ICON} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
<span className="mail-detail-toolbar-sep" aria-hidden="true" />
|
||||
<div className="mail-detail-toolbar-group">
|
||||
<ToolbarButton title="Move to folder" onClick={onMove} disabled={busy}>
|
||||
<FolderInput {...ICON} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton title="Apply labels" onClick={onMove} disabled={busy}>
|
||||
<Tag {...ICON} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton title="More actions" onClick={onMove} disabled={busy}>
|
||||
<MoreHorizontal {...ICON} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
{mailbox === "trash" && (
|
||||
<>
|
||||
<span className="mail-detail-toolbar-sep" aria-hidden="true" />
|
||||
<div className="mail-detail-toolbar-group">
|
||||
<ToolbarButton title="Restore from trash" onClick={onRestore} disabled={busy}>
|
||||
<RotateCcw {...ICON} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mail-detail-sender-row">
|
||||
<div className="mail-detail-sender-left">
|
||||
<SenderAvatar email={email} />
|
||||
<div className="mail-detail-sender-meta">
|
||||
<div className="mail-detail-sender-line">
|
||||
<strong>{senderIdentity(email)}</strong>
|
||||
</div>
|
||||
<button type="button" className="mail-detail-recipient-toggle">
|
||||
{recipientLabel(email, viewerEmail)} <ChevronDown size={12} strokeWidth={1.75} />
|
||||
</button>
|
||||
<div className="mail-detail-recipient-details">
|
||||
<small>To: {joinAddresses(email.recipients || email.to) || "Undisclosed"}</small>
|
||||
{joinAddresses(email.cc) && <small>Cc: {joinAddresses(email.cc)}</small>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mail-detail-sender-actions">
|
||||
<time dateTime={email.created_at}>
|
||||
{time.absolute}
|
||||
{time.relative ? ` (${time.relative})` : ""}
|
||||
</time>
|
||||
<button
|
||||
type="button"
|
||||
className={`mail-detail-icon-btn${email.is_starred ? " active" : ""}`}
|
||||
title={email.is_starred ? "Unstar" : "Star"}
|
||||
aria-label={email.is_starred ? "Unstar" : "Star"}
|
||||
onClick={() => onToggleStar?.(!email.is_starred)}
|
||||
>
|
||||
<Star size={16} strokeWidth={1.75} />
|
||||
</button>
|
||||
<button type="button" className="mail-detail-icon-btn" title="Reply" onClick={onReply}>
|
||||
<Reply size={16} strokeWidth={1.75} />
|
||||
</button>
|
||||
<button type="button" className="mail-detail-icon-btn" title="More" onClick={onMove}>
|
||||
<MoreHorizontal size={16} strokeWidth={1.75} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`mail-detail-body${body?.mode === "html" ? " is-html" : " is-text"}`}>
|
||||
{body?.mode === "html" ? (
|
||||
<div className="mail-detail-body-html" dangerouslySetInnerHTML={{ __html: body.value }} />
|
||||
) : (
|
||||
<div className="mail-detail-body-text">
|
||||
{plainTextParagraphs(body?.value || "").map((paragraph) => (
|
||||
<p key={paragraph.slice(0, 24)}>
|
||||
{paragraph.split("\n").map((line, index, lines) => (
|
||||
<span key={`${line}-${index}`}>
|
||||
{line}
|
||||
{index < lines.length - 1 && <br />}
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{attachments.length > 0 && (
|
||||
<div className="mail-detail-attachments" aria-label="Attachments">
|
||||
{attachments.map((file) => (
|
||||
<div key={`${file.filename}-${file.size}`} className="mail-detail-attachment">
|
||||
<span className="mail-detail-attachment-icon">📄</span>
|
||||
<div>
|
||||
<strong>{file.filename}</strong>
|
||||
<small>{file.content_type || "File"} · {formatFileSize(file.size)}</small>
|
||||
</div>
|
||||
<div className="mail-detail-attachment-actions">
|
||||
<button type="button">Preview</button>
|
||||
<button type="button">Download</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</header>
|
||||
|
||||
<footer className="mail-detail-reply-bar">
|
||||
<button type="button" className="mail-detail-reply-btn" onClick={onReply}>
|
||||
<Reply size={15} strokeWidth={1.75} /> Reply
|
||||
</button>
|
||||
{(email.recipients?.length > 1 || email.cc?.length) && (
|
||||
<button type="button" className="mail-detail-reply-btn subtle" onClick={onReply}>
|
||||
Reply all
|
||||
</button>
|
||||
)}
|
||||
<button type="button" className="mail-detail-reply-btn subtle" onClick={onForward}>
|
||||
<Forward size={15} strokeWidth={1.75} /> Forward
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
)}
|
||||
{loading && <div className="mail-detail-state">Opening message…</div>}
|
||||
{error && <div className="mail-detail-state error" role="alert">{error}</div>}
|
||||
|
||||
{email && !loading && (
|
||||
<div className="mail-detail-sheet">
|
||||
<div className="mail-detail-content">
|
||||
<div className="mail-detail-reading-column">
|
||||
<div className="mail-detail-subject-row">
|
||||
<h1>{email.subject?.trim() || "(No subject)"}</h1>
|
||||
<div className="mail-detail-labels">
|
||||
<span className="mail-detail-label-pill">{mailboxLabel(mailbox)}</span>
|
||||
{email.is_starred && <span className="mail-detail-label-pill starred">Starred</span>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mail-detail-ai-chips">
|
||||
<button type="button" className="mail-detail-ai-chip" onClick={onSummarizeThread}>
|
||||
<AgentSpark /> {summarizeLabel}
|
||||
</button>
|
||||
<button type="button" className="mail-detail-ai-chip" onClick={onExtractActions}>
|
||||
<AgentSpark /> Extract actions
|
||||
</button>
|
||||
{files > 0 && (
|
||||
<button type="button" className="mail-detail-ai-chip" onClick={onAnalyzeAttachment}>
|
||||
<AgentSpark /> Analyze attachment
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mail-detail-sender-row">
|
||||
<SenderAvatar email={email} />
|
||||
<div className="mail-detail-sender-meta">
|
||||
<div className="mail-detail-sender-line">
|
||||
<strong>{sender?.name}</strong>
|
||||
{sender?.address && <span className="mail-detail-sender-email"><{sender.address}></span>}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className={`mail-detail-recipient-toggle${headersOpen ? " open" : ""}`}
|
||||
onClick={() => setHeadersOpen((open) => !open)}
|
||||
aria-expanded={headersOpen}
|
||||
>
|
||||
{recipientLabel(email, viewerEmail)}
|
||||
<ChevronDown size={12} strokeWidth={1.75} />
|
||||
</button>
|
||||
{headersOpen && (
|
||||
<div className="mail-detail-recipient-details">
|
||||
<small>From: {joinAddresses(email.sender) || sender?.display}</small>
|
||||
<small>To: {joinAddresses(email.recipients || email.to) || "Undisclosed"}</small>
|
||||
{joinAddresses(email.cc) && <small>Cc: {joinAddresses(email.cc)}</small>}
|
||||
{joinAddresses(email.bcc) && <small>Bcc: {joinAddresses(email.bcc)}</small>}
|
||||
<small>Date: {time.absolute}{time.relative ? ` (${time.relative})` : ""}</small>
|
||||
<small>Subject: {email.subject?.trim() || "(No subject)"}</small>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mail-detail-sender-actions">
|
||||
<time dateTime={email.created_at}>
|
||||
{time.absolute}
|
||||
{time.relative ? ` (${time.relative})` : ""}
|
||||
</time>
|
||||
<button
|
||||
type="button"
|
||||
className={`mail-detail-icon-btn${email.is_starred ? " active" : ""}`}
|
||||
title={email.is_starred ? "Unstar" : "Star"}
|
||||
aria-label={email.is_starred ? "Unstar" : "Star"}
|
||||
onClick={() => onToggleStar?.(!email.is_starred)}
|
||||
>
|
||||
<Star size={16} strokeWidth={1.75} />
|
||||
</button>
|
||||
<button type="button" className="mail-detail-icon-btn" title="Reply" onClick={onReply}>
|
||||
<Reply size={16} strokeWidth={1.75} />
|
||||
</button>
|
||||
<button type="button" className="mail-detail-icon-btn" title="More actions" onClick={onMove}>
|
||||
<MoreHorizontal size={16} strokeWidth={1.75} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`mail-detail-body${body?.mode === "html" ? " is-html" : " is-text"}`}>
|
||||
{body?.mode === "html" ? (
|
||||
<div className="mail-detail-body-html" dangerouslySetInnerHTML={{ __html: body.value }} />
|
||||
) : (
|
||||
<div className="mail-detail-body-text">{body?.value}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{attachments.length > 0 && (
|
||||
<div className="mail-detail-attachments" aria-label="Attachments">
|
||||
{attachments.map((file) => (
|
||||
<div key={`${file.filename}-${file.size}`} className="mail-detail-attachment">
|
||||
<span className="mail-detail-attachment-icon">📄</span>
|
||||
<div>
|
||||
<strong>{file.filename}</strong>
|
||||
<small>{file.content_type || "File"} · {formatFileSize(file.size)}</small>
|
||||
</div>
|
||||
<div className="mail-detail-attachment-actions">
|
||||
<button type="button">Preview</button>
|
||||
<button type="button">Download</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<footer className="mail-detail-reply-bar">
|
||||
<button type="button" className="mail-detail-reply-btn" onClick={onReply}>
|
||||
<Reply size={15} strokeWidth={1.75} /> Reply
|
||||
</button>
|
||||
{showReplyAll ? (
|
||||
<button type="button" className="mail-detail-reply-btn subtle" onClick={onReply}>
|
||||
Reply all
|
||||
</button>
|
||||
) : null}
|
||||
<button type="button" className="mail-detail-reply-btn subtle" onClick={onForward}>
|
||||
<Forward size={15} strokeWidth={1.75} /> Forward
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -715,7 +715,6 @@ export default function MailboxView({
|
|||
onExtractActions={() => detail && extractActions(detail)}
|
||||
onSummarizeThread={() => detail && summarizeThread(detail)}
|
||||
onAnalyzeAttachment={() => detail && analyzeAttachment(detail)}
|
||||
onDraftReply={() => detail && extractActions(detail)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue