- Shipped
- September 16, 2026 at 4:01 PM UTC
- Author
- Kamo
- Commit
- 6ee727c
BulkEmailWorkspace takes the change frames useEmailCampaignsWebSocket now delivers (onChanged, d0b56adf). A frame names a campaign and what happened to it, never the campaign itself, so the list reads that one campaign again through GET /campaigns/{id} and puts it in place. A colleague's archive or restore moves the row between tabs here as it happens, a new or duplicated campaign appears, and a deleted one, or one that now answers 404, leaves the list. The staged outbox count follows the same frames. createCampaignChangeFollower keeps one read per campaign at a time. Frames that arrive while a campaign is being read (a list uploaded in slices announces every slice) ask for one more read once it returns, so a burst costs two reads and an older answer never lands on top of a newer one. A campaign deleted while its read was on the way is not put back when it returns. Checked in Chrome against the real component by pushing frames through the hook: a restore moved the row out of Archived with one read, a delete removed its row with none, a created draft appeared under Not Started, and five frames for one campaign cost two reads.