fix: show who a template addresses before inserting it and suggest the account's own categories

This commit is contained in:
Linus Rath
2026-10-28 07:34:05 +01:00
parent b5e92ba176
commit 63d64bba13
6 changed files with 34 additions and 5 deletions
+4 -1
View File
@@ -1561,7 +1561,9 @@ export function EmailComposer({
[composingAccountId],
);
// The admin may leave users only organisation and team templates.
const personalTemplatesHidden = useTemplateSources(composingAccountId).personalHidden;
const composingTemplates = useTemplateSources(composingAccountId);
const personalTemplatesHidden = composingTemplates.personalHidden;
const composingPersonalTemplates = composingTemplates.sources.find((source) => source.kind === 'personal')?.templates;
// Sign/encrypt is provided by crypto plugins (S/MIME, PGP) via the
// composer-toolbar slot + the onComposeSend hook — the host stays
// crypto-agnostic.
@@ -4873,6 +4875,7 @@ export function EmailComposer({
>
<h3 className="text-lg font-semibold text-foreground mb-4">{t('save_as_template')}</h3>
<TemplateForm
categorySource={composingPersonalTemplates}
initialData={{
subject,
body,
@@ -123,4 +123,12 @@ describe('template picker sources', () => {
expect(screen.getByText('Mine')).toBeInTheDocument();
expect(screen.queryByText("Dave's own")).toBeNull();
});
it('shows who a template addresses before it is inserted', () => {
useTemplateStore.setState({ templates: [own('mine', 'Mine', { defaultRecipients: { to: ['a@x.test', 'b@x.test'], cc: ['c@x.test'], bcc: ['hidden@x.test'] } })] });
render(<TemplatePicker isOpen onClose={() => {}} onSelect={() => {}} />);
const line = screen.getByTestId('template-recipients');
expect(line).toHaveTextContent('preview_to');
expect(line).toHaveTextContent('preview_cc');
});
});
+5 -2
View File
@@ -31,15 +31,18 @@ interface TemplateFormProps {
shared?: boolean;
/** A save is under way: the save button waits. */
busy?: boolean;
/** The templates whose categories the category field suggests; the browser's list when left out. */
categorySource?: EmailTemplate[];
}
export function TemplateForm({ template, initialData, onSave, onCancel, shared = false, busy = false }: TemplateFormProps) {
export function TemplateForm({ template, initialData, onSave, onCancel, shared = false, busy = false, categorySource }: TemplateFormProps) {
const t = useTranslations('templates');
const tSettings = useTranslations('settings.templates');
const tComposer = useTranslations('email_composer');
const { identities } = useAuthStore();
const templates = useTemplateStore((s) => s.templates);
const browserList = useTemplateStore((s) => s.templates);
const templates = categorySource ?? browserList;
const [name, setName] = useState(template?.name || '');
const [category, setCategory] = useState(template?.category || '');
+9
View File
@@ -113,6 +113,15 @@ export function TemplatePicker({ isOpen, onClose, onSelect, accountId }: Templat
{template.subject}
</p>
)}
{/* Who the template addresses, before it is put into the message. */}
{(template.defaultRecipients?.to?.length || template.defaultRecipients?.cc?.length) ? (
<p className="text-xs text-muted-foreground truncate mt-1" data-testid="template-recipients">
{[
template.defaultRecipients?.to?.length ? tOrg('preview_to', { list: template.defaultRecipients.to.join(', ') }) : null,
template.defaultRecipients?.cc?.length ? tOrg('preview_cc', { list: template.defaultRecipients.cc.join(', ') }) : null,
].filter(Boolean).join(' · ')}
</p>
) : null}
</button>
);
+4 -1
View File
@@ -2925,7 +2925,10 @@
"local_only_no_room": "Nur in diesem Browser: In Ihrem Konto ist kein Platz für weitere Vorlagen.",
"personal_in_account": "Ihre Vorlagen werden in Ihrem Konto gespeichert, Sie haben sie also auf jedem Gerät, auf dem Sie sich anmelden. Alle, für die Sie Ihren Posteingang freigeben, können sie lesen.",
"team_note": "Alle Mitglieder von {name} sehen diese Vorlagen und können sie ändern.",
"team_read_only": "Sie können diese Vorlagen verwenden, aber nicht ändern."
"team_read_only": "Sie können diese Vorlagen verwenden, aber nicht ändern.",
"save_failed_conflict": "Von jemand anderem geändert. Laden Sie neu und versuchen Sie es erneut.",
"preview_to": "An: {list}",
"preview_cc": "Cc: {list}"
},
"contacts": {
"directory": {
+4 -1
View File
@@ -2925,7 +2925,10 @@
"local_only_no_room": "Only in this browser: your account has no room for more templates.",
"personal_in_account": "Your templates are kept in your account, so you have them on every device you sign in on. Anyone you share your Inbox with can read them.",
"team_note": "Everyone in {name} sees these templates and can change them.",
"team_read_only": "You can use these templates but not change them."
"team_read_only": "You can use these templates but not change them.",
"save_failed_conflict": "Changed by someone else. Reload and try again.",
"preview_to": "To: {list}",
"preview_cc": "Cc: {list}"
},
"contacts": {
"directory": {