From 07c473e0575e2b060604a18bce182ed09a082618 Mon Sep 17 00:00:00 2001 From: Paulhenry Saux Date: Wed, 22 Jul 2026 13:39:21 +0200 Subject: [PATCH] feat: add new plugin ui.rerenderFetchedEmails method --- lib/plugin-sandbox/host-api.ts | 8 ++++++++ lib/plugin-sandbox/protocol.ts | 2 +- lib/plugin-sandbox/runtime.tsx | 1 + stores/email-store.ts | 12 ++++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/plugin-sandbox/host-api.ts b/lib/plugin-sandbox/host-api.ts index 882d84da..e40a8e4d 100644 --- a/lib/plugin-sandbox/host-api.ts +++ b/lib/plugin-sandbox/host-api.ts @@ -63,6 +63,7 @@ const PERM_PER_METHOD: Record = { 'ui.alert': null, 'ui.prompt': null, 'ui.rerenderEmail': null, + 'ui.rerenderFetchedEmails': null, 'ui.openExternalUrl': null, 'ui.downloadFile': 'ui:download-file' }; @@ -653,6 +654,13 @@ export async function dispatchApiCall( window.dispatchEvent(new CustomEvent('plugin:rerender-email')); return undefined; } + case 'ui.rerenderFetchedEmails': { + // Re-run the onEmailsFetched hook for the currently shown message list. + // Used by crypto plugins after they change decryption state s + // so the preview or others properties re-decrypts without a full reload. + window.dispatchEvent(new CustomEvent('plugin:rerender-fetched-emails')); + return undefined; + } case 'ui.openExternalUrl': { const url = String(args[0] ?? ''); // Only http(s) - the sandbox should not be able to navigate the host diff --git a/lib/plugin-sandbox/protocol.ts b/lib/plugin-sandbox/protocol.ts index 33a11f21..6dc901e2 100644 --- a/lib/plugin-sandbox/protocol.ts +++ b/lib/plugin-sandbox/protocol.ts @@ -246,7 +246,7 @@ export const API_METHODS = [ 'upfiles.get', 'upfiles.save', 'admin.getConfig', 'admin.getAllConfig', 'admin.setConfig', 'admin.deleteConfig', 'toast.success', 'toast.error', 'toast.info', 'toast.warning', - 'ui.confirm', 'ui.alert', 'ui.prompt', 'ui.rerenderEmail', 'ui.openExternalUrl', 'ui.downloadFile' + 'ui.confirm', 'ui.alert', 'ui.prompt', 'ui.rerenderEmail', 'ui.rerenderFetchedEmails', 'ui.openExternalUrl', 'ui.downloadFile' ] as const; export type ApiMethod = (typeof API_METHODS)[number]; diff --git a/lib/plugin-sandbox/runtime.tsx b/lib/plugin-sandbox/runtime.tsx index 3a698825..93ed82e8 100644 --- a/lib/plugin-sandbox/runtime.tsx +++ b/lib/plugin-sandbox/runtime.tsx @@ -241,6 +241,7 @@ function buildPluginApi(manifest: PluginManifest) { /** Re-runs the onRenderEmailBody hook for the open message (e.g. after a * crypto plugin unlocks a key) so its body re-renders without a reload. */ rerenderEmail: () => callApi('ui.rerenderEmail', []) as Promise, + rerenderFetchedEmails: () => callApi('ui.rerenderFetchedEmails', []) as Promise, /** Opens an http/https URL in a new tab via host `window.open`. */ openExternalUrl: (url: string, target?: string) => callApi('ui.openExternalUrl', [url, target]) as Promise, diff --git a/stores/email-store.ts b/stores/email-store.ts index 6ee083cf..4896544b 100644 --- a/stores/email-store.ts +++ b/stores/email-store.ts @@ -757,6 +757,18 @@ function findTrashMailbox( }); } +// Plugin re-render for already fetched emails. +// Plugins can trigger: window.dispatchEvent(new Event('plugin:rerender-fetched-emails')) +if (typeof window !== 'undefined') { + window.addEventListener('plugin:rerender-fetched-emails', async () => { + const state = useEmailStore.getState(); + const transformed = await emailHooks.onEmailsFetched.transform(state.emails); + useEmailStore.setState({ + emails: annotateScheduledEmails(transformed, state.scheduledSubmissionByEmailId), + }); + }); +} + export const useEmailStore = create((set, get) => ({ emails: [], mailboxes: [],