feat(staging): in-page preview of cryptify's notification email#244
Conversation
On staging, cryptify runs with `staging_mode = true` and logs notification emails instead of dispatching via SMTP — handy for safety, painful for testing because the only way to get the /download link was to scrape container logs. This wires up an automatic modal on the "files sent" screen that pulls the rendered email(s) from cryptify's new `GET /staging/preview/<uuid>` endpoint and embeds the HTML body in a sandboxed iframe. A header bar shows From / Reply-To / Subject / To with a switcher when there are multiple recipients (including the sender's confirmation copy). The email body itself is rendered by cryptify, not re-mocked here, so the two cannot drift. The runtime gate is a new `STAGING` flag on `window.APP_CONFIG`, injected by the postguard-ops ConfigMap from the existing `cryptify_staging_mode` Terraform variable. Production sees no UI change. Requires encryption4all/cryptify#171.
|
Dobby has received the request! Routing to the right specialist now... |
There was a problem hiding this comment.
Code review
Staging-only email-preview modal gated behind a runtime STAGING flag and a sandboxed iframe. svelte-check and build are clean; no correctness, security, or design blockers.
Rule compliance
One minor a11y note inline (wcag-aa-compliance): close button lacks a :focus-visible indicator. Non-blocking — it's a staging-only developer tool. All other rules pass.
| data = (await res.json()) as PreviewResponse | ||
| if (allEmails.length === 0) { | ||
| status = 'error' | ||
| errorMessage = $_('filesharing.emailPreview.empty') |
There was a problem hiding this comment.
[Code review] When fetch succeeds but allEmails is empty, status is set to 'error' and errorMessage is overwritten with the empty translation. The user then sees "Could not load preview." followed by "No recipients to preview." — two conflicting messages. Use a dedicated empty state, or render only the empty string.
| padding: 0 0.25rem; | ||
| } | ||
|
|
||
| .close-btn:hover { |
There was a problem hiding this comment.
[Rule: wcag-aa-compliance] .close-btn (and .backdrop-close) have :hover but no :focus-visible style, so keyboard users get no focus indicator. Add e.g. &:focus-visible { outline: 2px solid var(--pg-text); outline-offset: 2px; }. Non-blocking — staging-only surface.
- The "no recipients" path previously set `status = 'error'` and stuffed the empty-state copy into `errorMessage`, so the modal rendered "Could not load preview." followed by "No recipients to preview." — two conflicting messages. Add a distinct `'empty'` status and render the empty copy on its own. - Add `:focus-visible` outlines on `.close-btn` and `.backdrop-close` so keyboard users get a visible focus indicator on the only two interactive elements outside the content body.
Summary
/download?uuid=…link for testing has beenkubectl logs cryptify | grep STAGING.GET /staging/preview/<uuid>endpoint, drops the rendered HTML into a sandboxed iframe, and shows a header bar (From / Reply-To / Subject / To) with a recipient switcher (multi-recipient + the sender's confirmation copy).window.APP_CONFIG.STAGING. Wired in the ops repo from the existingcryptify_staging_modevariable. Production is unchanged.Requires encryption4all/cryptify#171 and the postguard-ops
STAGINGflag PR.Test plan
STAGING: trueinstatic/config.js, pointVITE_FILEHOST_URLat a cryptify withstaging_mode = true, send a 2-recipient share with "send me a confirmation", confirm the modal opens with three switcher entries (two recipients + sender copy)./downloadlink inside it works.STAGING: false), confirm the modal never appears.npx svelte-checkclean.