mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-02 00:14:23 +00:00
Merge pull request #861 from dealerweb/fix/fresh-compose-tab-title
fix: keep the fresh-compose tab title clear of the selected email subject
This commit is contained in:
@@ -94,7 +94,7 @@ import { plainTextToComposerBody, getQuoteBodies } from "@/lib/email-composer-ut
|
||||
import { appLifecycleHooks, uiHooks, routerHooks, toastHooks, emailHooks } from "@/lib/plugin-hooks";
|
||||
import { emailToReadView } from "@/lib/plugin-projection";
|
||||
import { buildQuoteHeader } from "@/lib/quote-header";
|
||||
import { buildReplySubject, buildForwardSubject } from "@/lib/subject-prefix";
|
||||
import { buildComposeTabTitle, buildReplySubject } from "@/lib/subject-prefix";
|
||||
import { buildForwardAsAttachmentPayload } from "@/lib/forward-as-attachment";
|
||||
import { getEffectiveLocale } from '@/i18n/detect-locale';
|
||||
import {
|
||||
@@ -913,17 +913,14 @@ export function MailApp({ linkSegments }: MailAppProps = {}) {
|
||||
} : undefined);
|
||||
|
||||
const effectiveMode = pendingDraft?.mode ?? composerMode;
|
||||
const baseSubject = (pendingDraft?.subject?.trim() || selectedEmail?.subject?.trim()) ?? '';
|
||||
let title = t('email_composer.new_message');
|
||||
if (baseSubject) {
|
||||
if (effectiveMode === 'reply' || effectiveMode === 'replyAll') {
|
||||
title = buildReplySubject(baseSubject, t('email_composer.prefix.reply'));
|
||||
} else if (effectiveMode === 'forward') {
|
||||
title = buildForwardSubject(baseSubject, t('email_composer.prefix.forward'));
|
||||
} else {
|
||||
title = baseSubject;
|
||||
}
|
||||
}
|
||||
const title = buildComposeTabTitle({
|
||||
mode: effectiveMode,
|
||||
draftSubject: pendingDraft?.subject,
|
||||
sourceSubject: selectedEmail?.subject,
|
||||
replyPrefix: t('email_composer.prefix.reply'),
|
||||
forwardPrefix: t('email_composer.prefix.forward'),
|
||||
fallback: t('email_composer.new_message'),
|
||||
});
|
||||
|
||||
useProTabStore.getState().openComposeTab({
|
||||
sessionId: composerSessionId + 1,
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
stripSubjectPrefixes,
|
||||
buildReplySubject,
|
||||
buildForwardSubject,
|
||||
buildComposeTabTitle,
|
||||
} from '@/lib/subject-prefix';
|
||||
|
||||
describe('stripSubjectPrefixes', () => {
|
||||
@@ -54,3 +55,33 @@ describe('buildReplySubject / buildForwardSubject', () => {
|
||||
expect(buildReplySubject('', 'AW:')).toBe('AW:');
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildComposeTabTitle', () => {
|
||||
const prefixes = { replyPrefix: 'Re:', forwardPrefix: 'Fwd:', fallback: 'New message' };
|
||||
|
||||
it('never inherits the selected email subject for a fresh compose (#856)', () => {
|
||||
expect(buildComposeTabTitle({ mode: 'compose', sourceSubject: 'Invoice August 2026', ...prefixes }))
|
||||
.toBe('New message');
|
||||
});
|
||||
|
||||
it('uses the draft subject for a fresh compose when one exists (mailto, draft edit)', () => {
|
||||
expect(buildComposeTabTitle({ mode: 'compose', draftSubject: 'Lunch?', sourceSubject: 'Invoice August 2026', ...prefixes }))
|
||||
.toBe('Lunch?');
|
||||
});
|
||||
|
||||
it('derives reply and forward titles from the source subject', () => {
|
||||
expect(buildComposeTabTitle({ mode: 'reply', sourceSubject: 'Invoice August 2026', ...prefixes }))
|
||||
.toBe('Re: Invoice August 2026');
|
||||
expect(buildComposeTabTitle({ mode: 'replyAll', sourceSubject: 'AW: Invoice', ...prefixes }))
|
||||
.toBe('Re: Invoice');
|
||||
expect(buildComposeTabTitle({ mode: 'forward', sourceSubject: 'Invoice', ...prefixes }))
|
||||
.toBe('Fwd: Invoice');
|
||||
});
|
||||
|
||||
it('prefers a resumed draft subject over the source and falls back when both are blank', () => {
|
||||
expect(buildComposeTabTitle({ mode: 'reply', draftSubject: 'Re: edited', sourceSubject: 'Original', ...prefixes }))
|
||||
.toBe('Re: edited');
|
||||
expect(buildComposeTabTitle({ mode: 'reply', sourceSubject: ' ', ...prefixes }))
|
||||
.toBe('New message');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -119,3 +119,26 @@ export function buildForwardSubject(subject: string | undefined | null, prefix:
|
||||
const stripped = stripSubjectPrefixes(subject);
|
||||
return stripped ? `${prefix} ${stripped}` : prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Working title for a compose tab. Reply and forward derive it from the
|
||||
* message they act on; a fresh compose must NOT inherit the subject of
|
||||
* whatever email happens to be selected in the list (#856) - it starts on
|
||||
* the fallback ("New message") until a draft subject exists.
|
||||
*/
|
||||
export function buildComposeTabTitle(opts: {
|
||||
mode: 'compose' | 'reply' | 'replyAll' | 'forward';
|
||||
draftSubject?: string | null;
|
||||
sourceSubject?: string | null;
|
||||
replyPrefix: string;
|
||||
forwardPrefix: string;
|
||||
fallback: string;
|
||||
}): string {
|
||||
const draft = opts.draftSubject?.trim() ?? '';
|
||||
const source = opts.mode === 'compose' ? '' : (opts.sourceSubject?.trim() ?? '');
|
||||
const base = draft || source;
|
||||
if (!base) return opts.fallback;
|
||||
if (opts.mode === 'reply' || opts.mode === 'replyAll') return buildReplySubject(base, opts.replyPrefix);
|
||||
if (opts.mode === 'forward') return buildForwardSubject(base, opts.forwardPrefix);
|
||||
return base;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user