fix: mark the answered or forwarded message from a Pro compose tab also when the send is held for undo

This commit is contained in:
Linus Rath
2026-10-26 16:14:18 +01:00
parent 89be5f1e5b
commit 4a9284aa48
2 changed files with 24 additions and 7 deletions
@@ -6,7 +6,7 @@ import React from 'react';
// login and account they are in. Email ids repeat across accounts and
// servers, so the active login's client would act on its own namesake.
const { activeClient, otherClient, markEmailKeyword, toastError } = vi.hoisted(() => {
const { activeClient, otherClient, markEmailKeyword, toastError, sendResult } = vi.hoisted(() => {
const makeClient = (label: string) => ({
label,
getAccountId: () => `jmap-${label}`,
@@ -18,6 +18,7 @@ const { activeClient, otherClient, markEmailKeyword, toastError } = vi.hoisted((
otherClient: makeClient('account-2'),
markEmailKeyword: vi.fn(async () => {}),
toastError: vi.fn(),
sendResult: { scheduled: false },
};
});
@@ -51,7 +52,7 @@ vi.mock('@/stores/auth-store', () => {
});
vi.mock('@/stores/email-store', () => {
const state = {
sendEmail: async () => ({ scheduled: false }),
sendEmail: async () => ({ ...sendResult }),
markEmailKeyword,
refreshCurrentMailbox: async () => {},
fetchScheduledEmails: async () => {},
@@ -77,6 +78,7 @@ import { ProComposeTabBody } from '../pro-compose-tab-body';
beforeEach(() => {
markEmailKeyword.mockClear();
sendResult.scheduled = false;
toastError.mockClear();
activeClient.deleteEmail.mockClear();
otherClient.deleteEmail.mockClear();
@@ -94,6 +96,19 @@ describe('Pro compose tab', () => {
expect(markEmailKeyword).toHaveBeenCalledWith(activeClient, 'eaaaaab', '$answered', source);
});
// A send delay (undo send) makes every send a held submission, so the
// flag has to be set before the held send returns, as in the inline composer (#985).
it.each([['reply', '$answered'], ['forward', '$forwarded']] as const)('flags the source of a held %s', async (mode, keyword) => {
sendResult.scheduled = true;
const source = { clientAccountId: 'local-2', accountId: 'jmap-group' };
render(<ProComposeTabBody tabId="t" data={{ sessionId: 1, mode, title: 'Re', sourceEmailId: 'eaaaaab', sourceEmailSource: source }} />);
fireEvent.click(screen.getByText('send'));
await waitFor(() => expect(markEmailKeyword).toHaveBeenCalled());
expect(markEmailKeyword).toHaveBeenCalledWith(activeClient, 'eaaaaab', keyword, source);
});
it('discards the draft through the login that saved it', async () => {
render(<ProComposeTabBody tabId="t" data={{ sessionId: 1, mode: 'compose', title: 'New' }} />);
+7 -5
View File
@@ -100,13 +100,10 @@ export function ProComposeTabBody({ tabId, data }: ProComposeTabBodyProps) {
}
announceSent(result, sendData);
if (result.scheduled) {
await handleScheduledSendCreated();
return;
}
// Mark the original message as $answered / $forwarded so the standard
// viewer and list reflect the action (same behaviour as inline compose).
// Before the scheduled return: with a send delay every send is a held
// submission, and the flag was never set. (#985)
if (data.sourceEmailId && (data.mode === 'reply' || data.mode === 'replyAll')) {
try {
await useEmailStore.getState().markEmailKeyword(client, data.sourceEmailId, '$answered', data.sourceEmailSource ?? undefined);
@@ -121,6 +118,11 @@ export function ProComposeTabBody({ tabId, data }: ProComposeTabBodyProps) {
}
}
if (result.scheduled) {
await handleScheduledSendCreated();
return;
}
// Refresh the currently-active mail list so the new sent message /
// updated keyword status shows up.
await refreshCurrentMailbox(client);