import { EnvelopeIcon, ExclamationCircleIcon, XMarkIcon } from "@heroicons/react/20/solid"; import { CheckCircleIcon } from "@heroicons/react/24/solid"; import { useSearchParams } from "@remix-run/react"; import { useEffect, useMemo } from "react"; import { useTypedLoaderData } from "remix-typedjson"; import { Toaster, toast } from "sonner"; import { type ToastMessageAction } from "~/models/message.server"; import { type loader } from "~/root"; import { cn } from "~/utils/cn"; import { Button, LinkButton } from "./Buttons"; import { Header2 } from "./Headers"; import { Paragraph } from "./Paragraph"; const defaultToastDuration = 5000; const permanentToastDuration = 60 * 60 * 24 * 1000; export function Toast() { const { toastMessage } = useTypedLoaderData(); useEffect(() => { if (!toastMessage) { return; } const { message, type, options } = toastMessage; const ephemeral = options.action ? false : options.ephemeral; toast.custom( (t) => ( ), { duration: ephemeral ? defaultToastDuration : permanentToastDuration, } ); }, [toastMessage]); return ; } export function useToast() { return useMemo( () => ({ success(message: string, options?: { title?: string; ephemeral?: boolean }) { const ephemeral = options?.ephemeral ?? true; toast.custom( (t) => ( ), { duration: ephemeral ? defaultToastDuration : permanentToastDuration } ); }, error(message: string, options?: { title?: string; ephemeral?: boolean }) { const ephemeral = options?.ephemeral ?? true; toast.custom( (t) => ( ), { duration: ephemeral ? defaultToastDuration : permanentToastDuration } ); }, }), [] ); } export function ToastUI({ variant, message, t, toastWidth = 356, // Default width, matches what sonner provides by default title, action, }: { variant: "error" | "success"; message: string; t: string; toastWidth?: string | number; title?: string; action?: ToastMessageAction; }) { return (
{variant === "success" ? ( ) : ( )}
{title && {title}} {message}
); } function Action({ action, toastId, className, }: { action?: ToastMessageAction; toastId: string; className?: string; }) { const [_, setSearchParams] = useSearchParams(); if (!action) return null; switch (action.action.type) { case "link": { return ( {action.label} ); } case "help": { const feedbackType = action.action.feedbackType; return ( ); } } }