feat(compose): preselect identity of the active mailbox for new messages
Starting a new message while viewing a specific mailbox/account now defaults the From identity to that mailbox instead of the global primary identity, so composing from info@ sends as info@. Mirrors the existing reply-time identity match and rides the same autoSelectReplyIdentity setting; reply/replyAll/forward keep resolving from the original recipients. Matches exact then +tag-stripped. Extracts findComposeIdentityId into lib/reply-identity.ts with unit tests.
This commit is contained in:
committed by
Linus Rath
parent
6c49427c7c
commit
b716f95a73
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { findReplyIdentityId, resolveReplyFrom } from '../reply-identity';
|
||||
import { findComposeIdentityId, findReplyIdentityId, resolveReplyFrom } from '../reply-identity';
|
||||
import type { Identity } from '../jmap/types';
|
||||
|
||||
const identities: Identity[] = [
|
||||
@@ -51,6 +51,29 @@ describe('findReplyIdentityId', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('findComposeIdentityId', () => {
|
||||
it('matches the identity of the active mailbox', () => {
|
||||
expect(findComposeIdentityId(identities, 'harry@secondary.com')).toBe('secondary');
|
||||
});
|
||||
|
||||
it('matches case-insensitively', () => {
|
||||
expect(findComposeIdentityId(identities, 'HARRY@PRIMARY.COM')).toBe('primary');
|
||||
});
|
||||
|
||||
it('strips +tag before matching', () => {
|
||||
expect(findComposeIdentityId(identities, 'harry+news@secondary.com')).toBe('secondary');
|
||||
});
|
||||
|
||||
it('returns null when the active mailbox has no matching identity', () => {
|
||||
expect(findComposeIdentityId(identities, 'other@example.com')).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null when no active mailbox email is given', () => {
|
||||
expect(findComposeIdentityId(identities, undefined)).toBeNull();
|
||||
expect(findComposeIdentityId(identities, '')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveReplyFrom', () => {
|
||||
it('returns the matching identity with no override when exact match', () => {
|
||||
expect(resolveReplyFrom(identities, { to: [{ email: 'harry@secondary.com' }] }))
|
||||
|
||||
@@ -67,6 +67,34 @@ export function findReplyIdentityId(
|
||||
return baseIdentity?.id ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick the identity to use for a NEW message started while viewing a specific
|
||||
* mailbox/account. Matches the active mailbox's address to a configured
|
||||
* identity (exact, then `+tag`-stripped) so composing from info@ defaults its
|
||||
* From to info@. Returns `null` when no address is given or none matches, so
|
||||
* the caller keeps the primary identity.
|
||||
*/
|
||||
export function findComposeIdentityId(
|
||||
identities: Identity[],
|
||||
accountEmail?: string | null,
|
||||
): string | null {
|
||||
const email = accountEmail?.trim();
|
||||
if (identities.length === 0 || !email) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const exact = normalizeEmailAddress(email);
|
||||
const exactIdentity = identities.find((identity) => normalizeEmailAddress(identity.email) === exact);
|
||||
if (exactIdentity) {
|
||||
return exactIdentity.id;
|
||||
}
|
||||
|
||||
const base = normalizeBaseEmailAddress(email);
|
||||
const baseIdentity = identities.find((identity) => normalizeBaseEmailAddress(identity.email) === base);
|
||||
|
||||
return baseIdentity?.id ?? null;
|
||||
}
|
||||
|
||||
export interface ReplyFromResolution {
|
||||
/** Identity to use for JMAP `identityId` and the SMTP envelope MAIL FROM. */
|
||||
identityId: string;
|
||||
|
||||
Reference in New Issue
Block a user