Skip to content
This repository was archived by the owner on Jul 30, 2026. It is now read-only.
This repository was archived by the owner on Jul 30, 2026. It is now read-only.

Redundant draft saveItem() round-trips in the compose encrypt path #131

Description

@dobby-coder

The compose encrypt path in src/taskpane/compose-view.ts calls saveItem() — a server round-trip on a draft that may carry a multi-MB encrypted body plus attachment — more often than it needs to.

encryptAndPrepareSend, compose-view.ts:512-537:

setStatus("Saving encrypted draft…");
await saveItem();                       // 517

state.encrypted = true;                 // 521-524: in-memory only
state.preEncryptBody = html;
state.encryptedAttachmentId = attachmentId;
state.encryptedSnapshot = relevantStateString();

const stampedRecipients = recipientsKey();
state.encryptedRecipientsHeader = stampedRecipients;
await saveItem();                       // 532
await setItemHeaders({ ... });
await saveItem();                       // 537

Nothing between line 517 and line 532 touches the item — only module-local state. The save at 532 exists because the "save, write header, save" idiom is copied from persistEncryptOnSend (compose-view.ts:61-63), where the leading save is what gives a fresh draft an itemId before a header write. Here the save at 517 has already done that, so 532 is a second full upload of the same draft for no benefit. The user waits through it twice in the slowest part of the flow.

Related, on the same idiom: persistEncryptedRecipients (compose-view.ts:70-83) also does save → setItemHeaders → save, and is called from renderToggleUI() (line 342), which runs on every recipient change and every policy-editor keystroke. It is guarded by the state.encryptedRecipientsHeader !== expected comparison so it only fires when the value actually changes, but each fire is still two full draft saves. Once the draft is encrypted, editing recipients back and forth flips expected between the key and null and pays that cost each time.

Worth a human decision rather than a mechanical edit, because the save calls are deliberate (see the comment at 512-515 about racing the upload against Send, and the "flushes the header change to the server so the OnMessageSend handler sees it" comment at 56-60) and because a wrong trim here means OnMessageSend reads a stale header — which fails toward blocking a send, but is still a regression in the flow.

Possible directions:

  • Drop the redundant await saveItem() at line 532; keep 517 (pre-header, gives the itemId) and 537 (flushes the header).
  • Factor the "save → set headers → save" idiom into one helper in src/lib/office-helpers.ts so the three call sites cannot drift, with a parameter for skipping the leading save when the caller has just saved.
  • For persistEncryptedRecipients, consider debouncing or deferring the header write until the compose view settles, instead of writing on each renderToggleUI() transition.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions