fix: pass email explicitly to handleForwardAsAttachment from the list

The list context-menu wiring called selectEmail(email) then invoked
handleForwardAsAttachment() synchronously in the same tick. Since
handleForwardAsAttachment read selectedEmail from its own closure, and
Zustand's store update doesn't propagate into this render's closure
until the next render, this could forward the previously selected
message (or no-op if nothing was selected yet) instead of the row the
user actually right-clicked.

Parameterize handleForwardAsAttachment to accept an explicit `email`
(defaulting to selectedEmail), the same pattern handleDelete already
uses in this file for the same class of problem, and pass it
explicitly from the list context-menu wiring. The EmailViewer overflow
menu's wiring is unaffected - it always operates on the single
currently-open email via the default parameter.

Caught by GitHub Copilot's automated PR review.
This commit is contained in:
Aaron Guise
2026-07-27 17:13:53 +12:00
parent 0563e88eb8
commit a34314cef5
+15 -7
View File
@@ -1559,8 +1559,16 @@ export default function Home() {
// by its existing blobId (no re-fetch/re-upload needed - JMAP blobs are // by its existing blobId (no re-fetch/re-upload needed - JMAP blobs are
// account-scoped, not per-email). Skips prepareComposerQuoteHeader // account-scoped, not per-email). Skips prepareComposerQuoteHeader
// entirely, so the body starts blank instead of quoting the original. // entirely, so the body starts blank instead of quoting the original.
const handleForwardAsAttachment = async () => { // Takes an explicit `email` (defaulting to selectedEmail), same pattern
if (!selectedEmail) return; // handleDelete uses just below, rather than always reading selectedEmail
// from this closure - callers that just called selectEmail(email) and
// invoke this synchronously in the same tick would otherwise see the
// PRE-update value (the Zustand store updates immediately, but this
// render's selectedEmail closure doesn't until the next render),
// forwarding the previously selected message or no-op'ing on an
// unselected row. See the list context-menu wiring below.
const handleForwardAsAttachment = async (email: Email | null = selectedEmail) => {
if (!email) return;
// Same filename options "Export as .eml" uses (see emailFilenameOptions // Same filename options "Export as .eml" uses (see emailFilenameOptions
// in email-viewer.tsx), so the two actions produce consistent filenames // in email-viewer.tsx), so the two actions produce consistent filenames
// for the same message rather than the synthetic attachment silently // for the same message rather than the synthetic attachment silently
@@ -1572,7 +1580,7 @@ export default function Home() {
filenameStripDiacritics, filenameStripDiacritics,
filenameCollapseSeparators, filenameCollapseSeparators,
} = useSettingsStore.getState(); } = useSettingsStore.getState();
const payload = buildForwardAsAttachmentPayload(selectedEmail, t('email_composer.prefix.forward'), { const payload = buildForwardAsAttachmentPayload(email, t('email_composer.prefix.forward'), {
template: emailDownloadTemplate, template: emailDownloadTemplate,
spaceReplacement: filenameSpaceReplacement, spaceReplacement: filenameSpaceReplacement,
lowercase: filenameLowercase, lowercase: filenameLowercase,
@@ -1582,8 +1590,8 @@ export default function Home() {
if (!payload) return; if (!payload) return;
const ok = await emailHooks.onBeforeForward.intercept({ const ok = await emailHooks.onBeforeForward.intercept({
originalEmailId: selectedEmail.id, originalEmailId: email.id,
originalEmail: emailToReadView(selectedEmail), originalEmail: emailToReadView(email),
mode: 'forward' as const, mode: 'forward' as const,
}); });
if (!ok) return; if (!ok) return;
@@ -1602,7 +1610,7 @@ export default function Home() {
mode: "forward", mode: "forward",
draftId: null, draftId: null,
replyTo: { replyTo: {
subject: selectedEmail.subject, subject: email.subject,
attachments: [payload.attachment], attachments: [payload.attachment],
}, },
}); });
@@ -3280,7 +3288,7 @@ export default function Home() {
}} }}
onForwardAsAttachment={(email) => { onForwardAsAttachment={(email) => {
selectEmail(email); selectEmail(email);
handleForwardAsAttachment(); handleForwardAsAttachment(email);
}} }}
onMarkAsRead={async (email, read) => { onMarkAsRead={async (email, read) => {
if (client) { if (client) {