fix: sync identity stores and append signatures to outgoing emails (#15)

- Add syncIdentities() to auth store to propagate identity changes from
  identity store, fixing stale data that caused save failures and duplicates
- Call syncIdentities() after every create, update, and delete in the
  identity manager modal
- Switch email composer to read identities from identity store for
  consistency with the rest of the app
- Append identity text signature (with RFC 3676 separator) to email body
  when sending from the composer and quick reply paths
- Add tests for syncIdentities and signature appending logic
This commit is contained in:
Linus Rath
2026-03-15 16:07:45 +01:00
parent 5643170220
commit 0080d27b3e
6 changed files with 214 additions and 3 deletions
+11 -2
View File
@@ -10,6 +10,7 @@ import { cn, formatFileSize } from "@/lib/utils";
import { debug } from "@/lib/debug";
import { toast } from "@/stores/toast-store";
import { useAuthStore } from "@/stores/auth-store";
import { useIdentityStore } from "@/stores/identity-store";
import { useContactStore } from "@/stores/contact-store";
import { useTemplateStore } from "@/stores/template-store";
import { SubAddressHelper } from "@/components/identity/sub-address-helper";
@@ -159,7 +160,9 @@ export function EmailComposer({
restoreFocus: true,
});
const { client, identities, primaryIdentity } = useAuthStore();
const { client } = useAuthStore();
const identities = useIdentityStore((s) => s.identities);
const primaryIdentity = identities[0] ?? null;
const getAutocomplete = useContactStore((s) => s.getAutocomplete);
const addTemplate = useTemplateStore((s) => s.addTemplate);
@@ -549,13 +552,19 @@ export function EmailComposer({
: currentIdentity.email
: undefined;
// Append signature from the selected identity
let finalBody = body;
if (currentIdentity?.textSignature) {
finalBody = body + '\n\n-- \n' + currentIdentity.textSignature;
}
try {
await onSend?.({
to: toAddresses,
cc: ccAddresses,
bcc: bccAddresses,
subject,
body,
body: finalBody,
draftId: finalDraftId || undefined,
fromEmail,
fromName: currentIdentity?.name || undefined,
@@ -9,6 +9,11 @@ import { ConfirmDialog } from '@/components/ui/confirm-dialog';
import { IdentityForm } from './identity-form';
import { useIdentityStore } from '@/stores/identity-store';
import { useAuthStore } from '@/stores/auth-store';
function useSyncIdentities() {
const syncIdentities = useAuthStore((state) => state.syncIdentities);
return syncIdentities;
}
import type { Identity, EmailAddress } from '@/lib/jmap/types';
import { toast } from '@/stores/toast-store';
import { useFocusTrap } from '@/hooks/use-focus-trap';
@@ -34,6 +39,7 @@ export function IdentityManagerModal({ isOpen, onClose }: IdentityManagerModalPr
const client = useAuthStore((state) => state.client);
const { identities, addIdentity, updateIdentityLocal, removeIdentity } = useIdentityStore();
const syncIdentities = useSyncIdentities();
const [editingId, setEditingId] = useState<string | null>(null);
const [isCreating, setIsCreating] = useState(false);
@@ -82,6 +88,7 @@ export function IdentityManagerModal({ isOpen, onClose }: IdentityManagerModalPr
);
addIdentity(newIdentity);
syncIdentities();
setIsCreating(false);
toast.success(tNotif('identity_created'));
} catch (error) {
@@ -104,6 +111,7 @@ export function IdentityManagerModal({ isOpen, onClose }: IdentityManagerModalPr
});
updateIdentityLocal(identity.id, data);
syncIdentities();
setEditingId(null);
toast.success(tNotif('identity_updated'));
} catch (error) {
@@ -133,6 +141,7 @@ export function IdentityManagerModal({ isOpen, onClose }: IdentityManagerModalPr
try {
await client.deleteIdentity(identity.id);
removeIdentity(identity.id);
syncIdentities();
toast.success(tNotif('identity_deleted'));
} catch (error) {
const message = error instanceof Error ? error.message : t('validation_errors.unknown_error');