From 89452871fbf3dd8945e1560af2cd86611e1c996e Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Wed, 14 Oct 2026 03:40:19 +0200 Subject: [PATCH] feat: tell plugins when an event reminder fires and when it is dismissed --- components/ui/toast.tsx | 20 +++- .../use-calendar-alerts-hooks.test.tsx | 95 +++++++++++++++++++ hooks/use-calendar-alerts.ts | 11 +++ 3 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 hooks/__tests__/use-calendar-alerts-hooks.test.tsx diff --git a/components/ui/toast.tsx b/components/ui/toast.tsx index 28c7dbd91..f385face2 100644 --- a/components/ui/toast.tsx +++ b/components/ui/toast.tsx @@ -19,6 +19,11 @@ export interface Toast { message?: string; duration?: number; onClick?: () => void; + /** + * Called when the user closes the toast: the close button, a click on the + * toast or on one of its actions. Not called when it times out. + */ + onDismiss?: () => void; icon?: React.ReactNode; action?: ToastAction; secondaryAction?: ToastAction; @@ -90,6 +95,15 @@ export function ToastItem({ toast, onClose }: ToastProps) { setTimeout(() => onClose(toast.id), 280); }, [onClose, toast.id]); + const dismissByUser = useCallback(() => { + try { + toast.onDismiss?.(); + } catch { + // A failing listener must not keep the toast on screen. + } + dismiss(); + }, [toast, dismiss]); + useEffect(() => { if (!toast.duration || toast.duration <= 0 || isProgress) return; @@ -112,7 +126,7 @@ export function ToastItem({ toast, onClose }: ToastProps) { e.stopPropagation(); try { action.onClick(); - dismiss(); + dismissByUser(); } catch { // Don't close toast on error so user can retry } @@ -141,7 +155,7 @@ export function ToastItem({ toast, onClose }: ToastProps) { onClick={() => { if (toast.onClick && !toast.action) { toast.onClick(); - dismiss(); + dismissByUser(); } }} > @@ -180,7 +194,7 @@ export function ToastItem({ toast, onClose }: ToastProps) {