Save contact photos inline, and load cards so avatars show #388

Closed
opened 2026-09-16 18:28:03 +00:00 by jcoffey-dev · 0 comments
Owner

Summary

Both problems in #376 are real, and they have separate causes.

1. Adding or changing a contact photo always failed.

  • Cause: the editor uploaded the cropped image and saved media with a blobId. That is the JMAP extension RFC 9610 allows, but Stalwart refuses it with invalidProperties on media: "blobIds in media is not supported".
  • Checked against a live 0.16.22 server with a throwaway account, whose test objects were removed afterwards:
    • a blobId photo is refused with exactly the reported error;
    • a data:image/jpeg;base64,… uri is accepted, on create and on update, and returned unchanged;
    • a 134 KB one was accepted.
  • Fix:
    • The editor saves the 256 px JPEG it already produces as a data: URI, with no upload.
    • A new withPhoto() replaces or removes only the photo. Saving used to replace the whole media map, which would also have dropped a logo or sound.
  • Mock: it now refuses a blobId in media with Stalwart's error, so this can't pass tests again.

2. Avatars in the mail list were missing after a refresh.

  • Cause: Avatar returned nothing unless the contacts store had its cards loaded, and init() only loaded address books. Cards were loaded only by opening Contacts, composing, the address menu, or calendar birthdays. So a photo showed once one of those had happened, and was gone after the next reload.
  • The photo form wasn't the cause: contactPhoto already reads data: URIs.
  • Fix:
    • init() loads the cards in the background.
    • Avatar uses whatever cards are held, including cards from shared books.
    • A card's photo is resolved against the account the card belongs to (accountOfCard), in the avatar and in the contacts view, instead of always the reader's own account.

KNOWN-ISSUES.md records the live result.

Fixes #376.

Related issues

Fixes #376.

Translations

Adds none. npm run i18n:check reports the same counts as main.

Testing

  • New tests:
    • withPhoto: inline form with no blobId; replacing a photo while keeping a logo; removing only the photo; the result read back by contactPhoto.
    • contacts-cards-at-start.test.ts: init() loads cards, so lookupByEmail finds a sender.
    • In account.test.ts, through the proxy and mock: a blobId photo is refused with Stalwart's message, and a data: URI is accepted.
  • npm test passes: 1,340 web tests and 250 server tests. npm run typecheck is clean.
  • Checked in Chrome against npm run dev:mock:
    1. Added a photo to Ada Lovelace through the editor's photo input. The save succeeded and the photo showed on the contact.
    2. The card was stored with media as { kind: "photo", uri: "data:image/jpeg;base64,…", mediaType }.
    3. Opened /mail/inbox fresh, without visiting Contacts. Every row from the contact's address ([email protected]) showed the photo as its avatar. Rows from [email protected], a different address that isn't on the card, correctly kept their initials.

Merged 2026-09-16 as coffey-labs/ihasmail@3d7602ce74

Rebuilt from: git history, session transcript.

## Summary Both problems in #376 are real, and they have separate causes. **1. Adding or changing a contact photo always failed.** - **Cause:** the editor uploaded the cropped image and saved `media` with a `blobId`. That is the JMAP extension RFC 9610 allows, but Stalwart refuses it with `invalidProperties` on `media`: "blobIds in media is not supported". - **Checked against a live 0.16.22 server** with a throwaway account, whose test objects were removed afterwards: - a `blobId` photo is refused with exactly the reported error; - a `data:image/jpeg;base64,…` `uri` is accepted, on create and on update, and returned unchanged; - a 134 KB one was accepted. - **Fix:** - The editor saves the 256 px JPEG it already produces as a `data:` URI, with no upload. - A new `withPhoto()` replaces or removes only the photo. Saving used to replace the whole `media` map, which would also have dropped a logo or sound. - **Mock:** it now refuses a `blobId` in `media` with Stalwart's error, so this can't pass tests again. **2. Avatars in the mail list were missing after a refresh.** - **Cause:** `Avatar` returned nothing unless the contacts store had its cards loaded, and `init()` only loaded address books. Cards were loaded only by opening Contacts, composing, the address menu, or calendar birthdays. So a photo showed once one of those had happened, and was gone after the next reload. - **The photo form wasn't the cause:** `contactPhoto` already reads `data:` URIs. - **Fix:** - `init()` loads the cards in the background. - `Avatar` uses whatever cards are held, including cards from shared books. - A card's photo is resolved against the account the card belongs to (`accountOfCard`), in the avatar and in the contacts view, instead of always the reader's own account. **KNOWN-ISSUES.md** records the live result. Fixes #376. ## Related issues Fixes #376. ## Translations Adds none. `npm run i18n:check` reports the same counts as `main`. ## Testing - **New tests:** - `withPhoto`: inline form with no `blobId`; replacing a photo while keeping a logo; removing only the photo; the result read back by `contactPhoto`. - `contacts-cards-at-start.test.ts`: `init()` loads cards, so `lookupByEmail` finds a sender. - In `account.test.ts`, through the proxy and mock: a `blobId` photo is refused with Stalwart's message, and a `data:` URI is accepted. - `npm test` passes: 1,340 web tests and 250 server tests. `npm run typecheck` is clean. - **Checked in Chrome against `npm run dev:mock`:** 1. Added a photo to Ada Lovelace through the editor's photo input. The save succeeded and the photo showed on the contact. 2. The card was stored with `media` as `{ kind: "photo", uri: "data:image/jpeg;base64,…", mediaType }`. 3. Opened `/mail/inbox` fresh, without visiting Contacts. Every row from the contact's address (`[email protected]`) showed the photo as its avatar. Rows from `[email protected]`, a different address that isn't on the card, correctly kept their initials. **Merged** 2026-09-16 as coffey-labs/ihasmail@3d7602ce7474 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.