diff --git a/package.json b/package.json index 9e77916d25..b1b9765b33 100644 --- a/package.json +++ b/package.json @@ -66,12 +66,12 @@ "react": "^18.2.0", "react-confetti": "^6.1.0", "react-dom": "^18.2.0", + "react-hot-toast": "^2.4.1", "react-i18next": "^12.3.1", "react-loading-skeleton": "^3.3.1", "react-modal": "^3.16.1", "react-qr-code": "^2.0.12", "react-router-dom": "^6.14.2", - "react-toastify": "^9.1.3", "slip77": "^0.2.0", "stream": "^0.0.2", "tailwindcss": "^3.3.3", diff --git a/src/app/components/InputCopyButton/index.tsx b/src/app/components/InputCopyButton/index.tsx index e8b8d63daa..274b6eb7c7 100644 --- a/src/app/components/InputCopyButton/index.tsx +++ b/src/app/components/InputCopyButton/index.tsx @@ -1,7 +1,7 @@ import { CopyIcon as CopyFilledIcon } from "@bitcoin-design/bitcoin-icons-react/filled"; import { CopyIcon } from "@bitcoin-design/bitcoin-icons-react/outline"; import { useState } from "react"; -import { toast } from "react-toastify"; +import toast from "~/app/components/Toast"; import { classNames } from "~/app/utils"; type Props = { diff --git a/src/app/components/QrcodeScanner/index.tsx b/src/app/components/QrcodeScanner/index.tsx index 1ed98920dd..a3c1b54c99 100644 --- a/src/app/components/QrcodeScanner/index.tsx +++ b/src/app/components/QrcodeScanner/index.tsx @@ -2,7 +2,7 @@ import { QrCodeIcon } from "@bitcoin-design/bitcoin-icons-react/filled"; import { Html5Qrcode, Html5QrcodeScannerState } from "html5-qrcode"; import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; -import { toast } from "react-toastify"; +import toast from "~/app/components/Toast"; import Button from "../Button"; diff --git a/src/app/components/SitePreferences/index.tsx b/src/app/components/SitePreferences/index.tsx index 64ddc0e04b..23f7c0f2f4 100644 --- a/src/app/components/SitePreferences/index.tsx +++ b/src/app/components/SitePreferences/index.tsx @@ -6,7 +6,7 @@ import Toggle from "@components/form/Toggle"; import type { FormEvent } from "react"; import { Fragment, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { toast } from "react-toastify"; +import toast from "~/app/components/Toast"; import { useAccount } from "~/app/context/AccountContext"; import { useSettings } from "~/app/context/SettingsContext"; import { PreferencesIcon } from "~/app/icons"; diff --git a/src/app/components/Toast/Toaster.tsx b/src/app/components/Toast/Toaster.tsx new file mode 100644 index 0000000000..a1568e0385 --- /dev/null +++ b/src/app/components/Toast/Toaster.tsx @@ -0,0 +1,12 @@ +import { Toaster as HotToaster } from "react-hot-toast"; + +export default function Toaster() { + return ( + + ); +} diff --git a/src/app/components/Toast/index.tsx b/src/app/components/Toast/index.tsx new file mode 100644 index 0000000000..e4247cbabd --- /dev/null +++ b/src/app/components/Toast/index.tsx @@ -0,0 +1,63 @@ +import { CrossIcon } from "@bitcoin-design/bitcoin-icons-react/outline"; +import { Transition } from "@headlessui/react"; +import { ReactNode } from "react"; +import { + CheckmarkIcon, + ErrorIcon, + ToastOptions, + toast as hotToast, +} from "react-hot-toast"; + +type ToastType = "error" | "success"; + +interface ToastMethods { + success: (message: string | ReactNode, options?: ToastOptions) => void; + error: (message: string | ReactNode, options?: ToastOptions) => void; + custom: ( + children: ReactNode, + type: ToastType, + options?: ToastOptions + ) => void; +} + +const toast: ToastMethods = { + success: (message: string | ReactNode, options?: ToastOptions) => { + toast.custom(message, "success", options); + }, + error: (message: string | ReactNode, options?: ToastOptions) => { + toast.custom(message, "error", { duration: options?.duration ?? 8_000 }); + }, + custom: (children: ReactNode, type: ToastType, options?: ToastOptions) => { + hotToast.custom( + (t: { visible: boolean; id: string }) => ( + +
+
+ {type == "success" && } + {type == "error" && } +
+
{children}
+ {/* Add close icons for toasts that are displayed for a longer time */} + {options?.duration && options?.duration > 10_000 && ( + hotToast.dismiss(t.id)} + /> + )} +
+
+ ), + options + ); + }, +}; + +export default toast; diff --git a/src/app/components/toasts/ConnectionErrorToast.tsx b/src/app/components/toasts/ConnectionErrorToast.tsx index ab9cd05318..ba0d2cb799 100644 --- a/src/app/components/toasts/ConnectionErrorToast.tsx +++ b/src/app/components/toasts/ConnectionErrorToast.tsx @@ -13,10 +13,11 @@ export default function ConnectionErrorToast({ const { t: tCommon } = useTranslation("common"); return ( <> -

- {tCommon("errors.connection_failed")} -
({message}) +

+ {tCommon("errors.connection_failed")} ( + {message})

+

{t("what_you_can_do")}