45 lines
1.6 KiB
TypeScript
45 lines
1.6 KiB
TypeScript
type MailAddressLookupCandidateLike = {
|
|
display_name: string;
|
|
email?: string | null;
|
|
};
|
|
|
|
export type MailAddressValue = {
|
|
name?: string | null;
|
|
email: string;
|
|
};
|
|
|
|
const EMAIL_PATTERN = /([^<>;,\s]+@[^<>;,\s]+)/g;
|
|
|
|
export function mailLookupSuggestions(candidates: readonly MailAddressLookupCandidateLike[]): MailAddressValue[] {
|
|
const seen = new Set<string>();
|
|
const suggestions: MailAddressValue[] = [];
|
|
for (const candidate of candidates) {
|
|
const email = String(candidate.email ?? "").trim().toLocaleLowerCase();
|
|
if (!email || seen.has(email)) continue;
|
|
seen.add(email);
|
|
suggestions.push({ name: candidate.display_name || email, email });
|
|
}
|
|
return suggestions;
|
|
}
|
|
|
|
export function mailboxHeaderAddresses(value?: string | null): MailAddressValue[] {
|
|
const input = String(value ?? "").trim();
|
|
if (!input) return [];
|
|
const results: MailAddressValue[] = [];
|
|
const seen = new Set<string>();
|
|
for (const match of input.matchAll(EMAIL_PATTERN)) {
|
|
const email = match[1]?.replace(/[)>]+$/, "").toLocaleLowerCase();
|
|
if (!email || seen.has(email)) continue;
|
|
seen.add(email);
|
|
const prefix = input.slice(Math.max(0, input.lastIndexOf(",", match.index) + 1), match.index).trim();
|
|
const name = prefix.replace(/[<"']/g, "").trim() || undefined;
|
|
results.push({ name, email });
|
|
}
|
|
return results;
|
|
}
|
|
|
|
export function mailtoHref(recipients: readonly MailAddressValue[]): string {
|
|
const addresses = recipients.map((recipient) => recipient.email.trim()).filter(Boolean);
|
|
return `mailto:${addresses.map(encodeURIComponent).join(",")}`;
|
|
}
|