diff --git a/components/email/email-composer.tsx b/components/email/email-composer.tsx index dda1f790..13a6b9f0 100644 --- a/components/email/email-composer.tsx +++ b/components/email/email-composer.tsx @@ -49,6 +49,8 @@ import { waitForPendingUploads, extractUserAuthoredText, type Recipient, + enrichChipsWithColorsAndIcons, + ICON_MAP, } from "@/lib/email-composer-utils"; import { isValidEmail } from "@/lib/validation"; import { RichTextEditor } from "@/components/email/rich-text-editor"; @@ -826,6 +828,26 @@ export function EmailComposer({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [composerClient, plainTextMode, mode]); + const processEnrichment = async ( + recipients: Recipient[], + setRecipients: (items: Recipient[]) => void + ) => { + const hasUnenriched = recipients.some((r) => !r.extra?.enriched); + if (!hasUnenriched) return; + + const newChips = await enrichChipsWithColorsAndIcons(recipients); + const fullyEnriched = newChips.map((chip) => ({ + ...chip, + extra: { ...chip.extra, enriched: true }, + })); + setRecipients(fullyEnriched); + }; + + + useEffect(() => { processEnrichment(to, setTo); }, [to]); + useEffect(() => { processEnrichment(cc, setCc); }, [cc]); + useEffect(() => { processEnrichment(bcc, setBcc); }, [bcc]); + const composerSignatureHtml = signatureIdentity?.htmlSignature ? `
${sanitizeSignatureHtmlForDisplay(signatureIdentity.htmlSignature)}
` : signatureIdentity?.textSignature @@ -3169,6 +3191,11 @@ function RecipientChipInput({ const handleContainerDrop = (e: React.DragEvent) => { performDrop(e, dropIndex ?? chips.length); }; + const colorStyles: Record<'success' | 'destructive' | 'warning', string> = { + success: "bg-success/15 text-secondary-foreground hover:bg-success/30 !border-success", + destructive: "bg-destructive/15 text-secondary-foreground hover:bg-destructive/30 !border-destructive", + warning: "bg-warning/15 text-secondary-foreground hover:bg-warning/30 !border-warning", + }; return (
@@ -3186,6 +3213,12 @@ function RecipientChipInput({ {chips.map((chip, i) => { const isEditing = editingChip?.index === i; const chipDisplay = formatChipDisplay(chip); + let IconComponent = null; + if(chip.extra?.icon){ + IconComponent = ICON_MAP[chip.extra?.icon]; + } + const customColor = chip.extra?.color; + return ( {dropIndex === i && ( @@ -3214,11 +3247,17 @@ function RecipientChipInput({ "inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-sm border border-border transition-colors", isEditing ? "bg-background ring-1 ring-ring" - : "bg-secondary text-secondary-foreground hover:bg-accent cursor-grab active:cursor-grabbing", + : ( customColor && colorStyles[customColor] + ? `${colorStyles[customColor]} cursor-grab active:cursor-grabbing` + : "bg-secondary text-secondary-foreground hover:bg-accent cursor-grab active:cursor-grabbing"), !isEditing && draggingIndex === i && "opacity-50" )} onContextMenu={isEditing ? undefined : (e) => handleContextMenu(e, i, chip)} > + {IconComponent ? ( + + ) : null} + {isEditing ? ( }; + extra?: { + color?: "success" | "destructive" | "warning"; // optional color for display purposes. May be populated by plugins via the onRecipientChipsChange hook. + icon?: IconName; // optional icon for display purposes. May be populated by plugins via the onRecipientChipsChange hook. + enriched?: boolean; // optional flag to indicate if the recipient has been enriched by plugins via the onRecipientChipsChange hook. + }; +}; + +/** Enriches recipient chips with colors and icons. */ +export async function enrichChipsWithColorsAndIcons(chips: Recipient[]): Promise { + return await emailHooks.onRecipientChipsChange.transform(chips); }; /** diff --git a/lib/plugin-hooks.ts b/lib/plugin-hooks.ts index 02fb8107..a298e933 100644 --- a/lib/plugin-hooks.ts +++ b/lib/plugin-hooks.ts @@ -276,6 +276,10 @@ export const emailHooks = { // lets plugin edit emails before they are shown in row. Used to populate preview // field for encryption plugins. onEmailsFetched: new HookBus(), + // Transform hook - lets plugins edit the recipient chips in composer fields. + // They can add, remove, or modify chips (e.g. rewrite addresses, add colors/icons). + // Take Recipient[] as argument. + onRecipientChipsChange: new HookBus(), }; // ยง7.2 Calendar Hooks