diff --git a/src/lib/components/filesharing/Done.svelte b/src/lib/components/filesharing/Done.svelte
index 765bfd9..1359594 100644
--- a/src/lib/components/filesharing/Done.svelte
+++ b/src/lib/components/filesharing/Done.svelte
@@ -4,7 +4,9 @@
import HelpToggle from '$lib/components/HelpToggle.svelte'
import Chip from '$lib/components/Chip.svelte'
import FileList from '$lib/components/filesharing/FileList.svelte'
+ import EmailPreviewModal from '$lib/components/filesharing/EmailPreviewModal.svelte'
import airplane from '$lib/assets/images/airplane.svg'
+ import { STAGING } from '$lib/env'
interface props {
encryptState: EncryptState
@@ -12,6 +14,13 @@
}
let { encryptState = $bindable(), createDefaultEncryptState }: props =
$props()
+
+ // On staging, cryptify renders the notification email but does not
+ // dispatch via SMTP. Auto-open the preview modal so developers don't
+ // have to scrape cryptify logs for the /download link. The modal
+ // fetches the actual rendered HTML from cryptify, so this branch
+ // never runs in production (STAGING is false there).
+ let previewOpen = $state(STAGING && !!encryptState.uploadUuid)
@@ -59,12 +68,28 @@
variant="dark"
/>
+ {#if STAGING && encryptState.uploadUuid}
+
(previewOpen = true)}
+ size="lg"
+ variant="default"
+ />
+ {/if}
+
+{#if STAGING && previewOpen && encryptState.uploadUuid}
+ (previewOpen = false)}
+ />
+{/if}
+
diff --git a/src/lib/components/filesharing/SendButton.svelte b/src/lib/components/filesharing/SendButton.svelte
index 15dc88b..7bbf1d1 100644
--- a/src/lib/components/filesharing/SendButton.svelte
+++ b/src/lib/components/filesharing/SendButton.svelte
@@ -192,6 +192,13 @@
message: encryptState.message,
language: lang as 'EN' | 'NL',
},
+ // Captured here (not from the upload() return value) so the
+ // staging email-preview modal can render even if a later
+ // chunk fails — the uuid is what links the preview to the
+ // /download?uuid=… link cryptify would have emailed.
+ onUploadInit: ({ uuid }) => {
+ encryptState.uploadUuid = uuid
+ },
})
const totalBytes = encryptState.files.reduce(
diff --git a/src/lib/env.ts b/src/lib/env.ts
index 5e4f28f..8c8230c 100644
--- a/src/lib/env.ts
+++ b/src/lib/env.ts
@@ -29,3 +29,10 @@ function runtimeConfig(): Record {
export const FF_BUSINESS = runtimeConfig().FF_BUSINESS === true
export const BUSINESS_URL =
(runtimeConfig().BUSINESS_URL as string) ?? 'https://business.postguard.eu'
+
+/** True on staging/dev where cryptify runs with `staging_mode = true` and
+ * does not actually send notification emails. The website uses this to
+ * render an in-page preview of the email a recipient would have received,
+ * so developers can grab the download link without trawling the cryptify
+ * logs. */
+export const STAGING = runtimeConfig().STAGING === true
diff --git a/src/lib/locales/en.json b/src/lib/locales/en.json
index d2eca62..f33920d 100644
--- a/src/lib/locales/en.json
+++ b/src/lib/locales/en.json
@@ -229,6 +229,21 @@
"addRecipient": "recipient",
"addRecipientButton": "Add another recipient",
"emailSenderConfirm": "Send me a confirmation",
+ "emailPreview": {
+ "title": "Notification email preview",
+ "subtitle": "Staging only — cryptify does not actually send notification emails on staging. This is what each recipient would have received.",
+ "loading": "Loading preview…",
+ "error": "Could not load preview.",
+ "empty": "No recipients to preview.",
+ "close": "Close",
+ "from": "From",
+ "replyTo": "Reply to",
+ "subject": "Subject",
+ "to": "To",
+ "confirmationTag": "sender copy",
+ "iframeTitle": "Notification email body",
+ "reopen": "Show email preview"
+ },
"timeremaining": {
"estimate": "Estimating...",
"unknown": "More then one day left.",
diff --git a/src/lib/locales/nl.json b/src/lib/locales/nl.json
index dbba7c0..da80cfe 100644
--- a/src/lib/locales/nl.json
+++ b/src/lib/locales/nl.json
@@ -228,6 +228,21 @@
"addRecipient": "ontvanger",
"addRecipientButton": "Voeg nog een ontvanger toe",
"emailSenderConfirm": "Stuur mij een bevestiging",
+ "emailPreview": {
+ "title": "Voorbeeld notificatie-e-mail",
+ "subtitle": "Alleen staging — cryptify verstuurt op staging geen e-mails. Dit is wat elke ontvanger zou hebben gezien.",
+ "loading": "Voorbeeld laden…",
+ "error": "Voorbeeld kon niet worden geladen.",
+ "empty": "Geen ontvangers om weer te geven.",
+ "close": "Sluiten",
+ "from": "Van",
+ "replyTo": "Antwoord aan",
+ "subject": "Onderwerp",
+ "to": "Aan",
+ "confirmationTag": "bevestigingskopie",
+ "iframeTitle": "Inhoud van de notificatie-e-mail",
+ "reopen": "Toon e-mailvoorbeeld"
+ },
"timeremaining": {
"estimate": "Estimating...",
"unknown": "Nog meer dan een dag",
diff --git a/src/lib/types/filesharing/attributes.ts b/src/lib/types/filesharing/attributes.ts
index 9a19d1d..c1f8be8 100644
--- a/src/lib/types/filesharing/attributes.ts
+++ b/src/lib/types/filesharing/attributes.ts
@@ -26,6 +26,9 @@ export type EncryptState = {
selfAborted: boolean
serverError: boolean
encryptStartTime: number
+ /** Cryptify upload UUID, captured via `onUploadInit` as soon as the
+ * server allocates one. Used by the staging email-preview modal. */
+ uploadUuid?: string
}
export type AttType =
diff --git a/static/config.js b/static/config.js
index 912cdd0..308552c 100644
--- a/static/config.js
+++ b/static/config.js
@@ -3,4 +3,8 @@
window.APP_CONFIG = {
FF_BUSINESS: true,
BUSINESS_URL: 'https://business.staging.postguard.eu',
+ // Set to true to simulate the staging/dev environment locally: instead
+ // of relying on cryptify to send a real notification email, the website
+ // pops up a preview of what the email would have looked like.
+ STAGING: false,
};