Skip to content

feat(staging): in-page preview of cryptify's notification email#244

Merged
rubenhensen merged 2 commits into
mainfrom
feat/staging-email-preview
Jun 3, 2026
Merged

feat(staging): in-page preview of cryptify's notification email#244
rubenhensen merged 2 commits into
mainfrom
feat/staging-email-preview

Conversation

@rubenhensen
Copy link
Copy Markdown
Contributor

Summary

  • On staging, cryptify renders notification emails but does not deliver them — so the only way to grab the /download?uuid=… link for testing has been kubectl logs cryptify | grep STAGING.
  • After a successful upload the "files sent" screen now auto-pops a modal that calls cryptify's new 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).
  • The email body itself is rendered by cryptify — no template duplicated client-side, so the two can't drift.
  • Gate: window.APP_CONFIG.STAGING. Wired in the ops repo from the existing cryptify_staging_mode variable. Production is unchanged.

Requires encryption4all/cryptify#171 and the postguard-ops STAGING flag PR.

Test plan

  • Locally: flip STAGING: true in static/config.js, point VITE_FILEHOST_URL at a cryptify with staging_mode = true, send a 2-recipient share with "send me a confirmation", confirm the modal opens with three switcher entries (two recipients + sender copy).
  • On staging deploy: send a share, confirm the preview opens and the /download link inside it works.
  • In production-equivalent (STAGING: false), confirm the modal never appears.
  • npx svelte-check clean.

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-coder
Copy link
Copy Markdown
Contributor

dobby-coder Bot commented Jun 3, 2026

Dobby has received the request! Routing to the right specialist now...

Copy link
Copy Markdown
Contributor

@dobby-coder dobby-coder Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant