diff --git a/src/entries/popup/components/Toast/Toast.tsx b/src/entries/popup/components/Toast/Toast.tsx index 6b37ceef54..d3bf5b13ba 100644 --- a/src/entries/popup/components/Toast/Toast.tsx +++ b/src/entries/popup/components/Toast/Toast.tsx @@ -1,33 +1,47 @@ +import EventEmitter from 'events'; + import React, { useEffect, useState } from 'react'; import { Box, Inline, Row, Rows, Text } from '~/design-system'; -import { useToast } from '../../hooks/useToast'; import { zIndexes } from '../../utils/zIndexes'; +const eventEmitter = new EventEmitter(); + +type ToastInfo = { title: string; description?: string }; + +const toastListener = ( + callback: ({ title, description }: ToastInfo) => void, +) => { + eventEmitter.addListener('rainbow_toast', callback); + return () => { + eventEmitter.removeListener('rainbow_toast', callback); + }; +}; + +export const triggerToast = ({ title, description }: ToastInfo) => { + eventEmitter.emit('rainbow_toast', { title, description }); +}; + export const Toast = () => { - const [visible, setVisible] = useState(false); - const [text, setText] = useState<{ title: string; description?: string }>({ - title: '', - description: '', - }); - const { listenToast, clearToastListener } = useToast(); + const [toastInfo, setToastInfo] = useState(null); - listenToast( - async ({ title, description }: { title: string; description?: string }) => { - setText({ title, description }); - setVisible(true); - setTimeout(() => { - setVisible(false); + useEffect(() => { + let timeout: NodeJS.Timeout; + const clearToastListener = toastListener(({ title, description }) => { + setToastInfo({ title, description }); + timeout = setTimeout(() => { + setToastInfo(null); }, 3000); - }, - ); + }); - useEffect(() => { - return () => clearToastListener(); - }, [clearToastListener]); + return () => { + clearToastListener(); + clearTimeout(timeout); + }; + }, []); - if (!visible) return null; + if (!toastInfo) return null; return ( { - {text.title} + {toastInfo.title} - {text.description && ( + {toastInfo.description && ( { weight="medium" align="center" > - {text.description} + {toastInfo.description} )} diff --git a/src/entries/popup/hooks/useHomeShortcuts.ts b/src/entries/popup/hooks/useHomeShortcuts.ts index 115b06f22d..e8c5003c7a 100644 --- a/src/entries/popup/hooks/useHomeShortcuts.ts +++ b/src/entries/popup/hooks/useHomeShortcuts.ts @@ -10,13 +10,13 @@ import { useSelectedTransactionStore } from '~/core/state/selectedTransaction'; import { truncateAddress } from '~/core/utils/address'; import { getProfileUrl, goToNewTab } from '~/core/utils/tabs'; +import { triggerToast } from '../components/Toast/Toast'; import * as wallet from '../handlers/wallet'; import { ROUTES } from '../urls'; import { clickHeaderRight } from '../utils/clickHeader'; import { useKeyboardShortcut } from './useKeyboardShortcut'; import { useRainbowNavigate } from './useRainbowNavigate'; -import { useToast } from './useToast'; export function useHomeShortcuts() { const { currentAddress: address } = useCurrentAddressStore(); @@ -24,7 +24,6 @@ export function useHomeShortcuts() { const { selectedToken } = useSelectedTokenStore(); const { selectedTransaction } = useSelectedTransactionStore(); const { sheet } = useCurrentHomeSheetStore(); - const { triggerToast } = useToast(); const getHomeShortcutsAreActive = useCallback(() => { return sheet === 'none' && !selectedTransaction && !selectedToken; @@ -36,7 +35,7 @@ export function useHomeShortcuts() { title: i18n.t('wallet_header.copy_toast'), description: truncateAddress(address), }); - }, [address, triggerToast]); + }, [address]); const openProfile = useCallback( () => diff --git a/src/entries/popup/hooks/useToast.ts b/src/entries/popup/hooks/useToast.ts deleted file mode 100644 index 1e9629d57a..0000000000 --- a/src/entries/popup/hooks/useToast.ts +++ /dev/null @@ -1,35 +0,0 @@ -import EventEmitter from 'events'; - -import { useCallback } from 'react'; - -const eventEmitter = new EventEmitter(); - -export const useToast = () => { - const listenToast = useCallback( - ( - callback: ({ - title, - description, - }: { - title: string; - description: string; - }) => Promise, - ) => { - eventEmitter.addListener('rainbow_toast', callback); - }, - [], - ); - - const triggerToast = useCallback( - ({ title, description }: { title: string; description?: string }) => { - eventEmitter.emit('rainbow_toast', { title, description }); - }, - [], - ); - - const clearToastListener = useCallback(() => { - eventEmitter.removeAllListeners('rainbow_toast'); - }, []); - - return { listenToast, triggerToast, clearToastListener }; -}; diff --git a/src/entries/popup/pages/home/Header.tsx b/src/entries/popup/pages/home/Header.tsx index cb57bf6415..8b4ff8b8f2 100644 --- a/src/entries/popup/pages/home/Header.tsx +++ b/src/entries/popup/pages/home/Header.tsx @@ -14,9 +14,9 @@ import { BoxStyles, TextStyles } from '~/design-system/styles/core.css'; import { AccountName } from '../../components/AccountName/AccountName'; import { Avatar } from '../../components/Avatar/Avatar'; +import { triggerToast } from '../../components/Toast/Toast'; import { useAlert } from '../../hooks/useAlert'; import { useAvatar } from '../../hooks/useAvatar'; -import { useToast } from '../../hooks/useToast'; import { useWallets } from '../../hooks/useWallets'; import { ROUTES } from '../../urls'; import { tabIndexes } from '../../utils/tabIndexes'; @@ -89,7 +89,6 @@ function ActionButtonsSection() { const { avatar } = useAvatar({ address }); const { isWatchingWallet } = useWallets(); - const { triggerToast } = useToast(); const { featureFlags } = useFeatureFlagsStore(); const { triggerAlert } = useAlert(); @@ -99,7 +98,7 @@ function ActionButtonsSection() { title: i18n.t('wallet_header.copy_toast'), description: truncateAddress(address), }); - }, [address, triggerToast]); + }, [address]); const allowSwap = React.useMemo( () => diff --git a/src/entries/popup/pages/home/TransactionDetailsMenu.tsx b/src/entries/popup/pages/home/TransactionDetailsMenu.tsx index 154431b736..674e9b710b 100644 --- a/src/entries/popup/pages/home/TransactionDetailsMenu.tsx +++ b/src/entries/popup/pages/home/TransactionDetailsMenu.tsx @@ -29,7 +29,7 @@ import { DetailsMenuRow, DetailsMenuWrapper, } from '../../components/DetailsMenu'; -import { useToast } from '../../hooks/useToast'; +import { triggerToast } from '../../components/Toast/Toast'; export function TransactionDetailsMenu({ children, @@ -44,7 +44,6 @@ export function TransactionDetailsMenu({ const [closed, setClosed] = useState(false); const onOpenChange = () => setClosed(false); - const { triggerToast } = useToast(); const trimmedHash = useMemo( () => transaction?.hash?.replace(/-.*/g, '') || '', [transaction], @@ -60,7 +59,7 @@ export function TransactionDetailsMenu({ title: i18n.t('speed_up_and_cancel.handle_copy_title'), description: truncatedAddress, }); - }, [triggerToast, trimmedHash, truncatedAddress]); + }, [trimmedHash, truncatedAddress]); const viewOnExplorer = useCallback(() => { const explorer = getTransactionBlockExplorerUrl({ diff --git a/src/entries/popup/pages/qrcode/index.tsx b/src/entries/popup/pages/qrcode/index.tsx index acd22c41bd..2fd56e1803 100644 --- a/src/entries/popup/pages/qrcode/index.tsx +++ b/src/entries/popup/pages/qrcode/index.tsx @@ -6,20 +6,20 @@ import { truncateAddress } from '~/core/utils/address'; import { Box, Button, Stack, Text } from '~/design-system'; import { AccountName } from '../../components/AccountName/AccountName'; -import { useToast } from '../../hooks/useToast'; +import { triggerToast } from '../../components/Toast/Toast'; import { QRCode } from './qrcode'; export const QRCodePage = () => { const { address } = useAccount(); - const { triggerToast } = useToast(); + const handleCopy = React.useCallback(() => { navigator.clipboard.writeText(address as string); triggerToast({ title: i18n.t('wallet_header.copy_toast'), description: truncateAddress(address), }); - }, [address, triggerToast]); + }, [address]); return ( { const init = async () => { @@ -43,7 +42,7 @@ export function SeedReveal() { triggerToast({ title: i18n.t('seed_reveal.phrase_copied'), }); - }, [seed, triggerToast]); + }, [seed]); return ( diff --git a/src/entries/popup/pages/settings/privacy/walletsAndKeys/recoveryPhrase/recoveryPhrase.tsx b/src/entries/popup/pages/settings/privacy/walletsAndKeys/recoveryPhrase/recoveryPhrase.tsx index e885827aed..90166fc69d 100644 --- a/src/entries/popup/pages/settings/privacy/walletsAndKeys/recoveryPhrase/recoveryPhrase.tsx +++ b/src/entries/popup/pages/settings/privacy/walletsAndKeys/recoveryPhrase/recoveryPhrase.tsx @@ -3,16 +3,15 @@ import { useLocation } from 'react-router-dom'; import { i18n } from '~/core/languages'; import SeedPhraseTable from '~/entries/popup/components/SeedPhraseTable/SeedPhraseTable'; +import { triggerToast } from '~/entries/popup/components/Toast/Toast'; import ViewSecret from '~/entries/popup/components/ViewSecret/ViewSecret'; import { exportWallet } from '~/entries/popup/handlers/wallet'; import { useRainbowNavigate } from '~/entries/popup/hooks/useRainbowNavigate'; -import { useToast } from '~/entries/popup/hooks/useToast'; import { ROUTES } from '~/entries/popup/urls'; export function RecoveryPhrase() { const { state } = useLocation(); const navigate = useRainbowNavigate(); - const { triggerToast } = useToast(); const [seed, setSeed] = useState(''); @@ -28,7 +27,7 @@ export function RecoveryPhrase() { 'settings.privacy_and_security.wallets_and_keys.recovery_phrase.phrase_copied', ), }); - }, [seed, triggerToast]); + }, [seed]); useEffect(() => { const fetchRecoveryPhrase = async () => { diff --git a/src/entries/popup/pages/walletSwitcher/index.tsx b/src/entries/popup/pages/walletSwitcher/index.tsx index 7a237297c9..a165a27be0 100644 --- a/src/entries/popup/pages/walletSwitcher/index.tsx +++ b/src/entries/popup/pages/walletSwitcher/index.tsx @@ -42,6 +42,7 @@ import { MoreInfoOption, } from '../../components/MoreInfoButton/MoreInfoButton'; import { QuickPromo } from '../../components/QuickPromo/QuickPromo'; +import { triggerToast } from '../../components/Toast/Toast'; import { getWallet, remove, wipe } from '../../handlers/wallet'; import { useAvatar } from '../../hooks/useAvatar'; import { useRainbowNavigate } from '../../hooks/useRainbowNavigate'; @@ -95,6 +96,10 @@ const infoButtonOptions = ({ { onSelect: () => { navigator.clipboard.writeText(account.address as string); + triggerToast({ + title: i18n.t('wallet_header.copy_toast'), + description: truncateAddress(account.address), + }); }, label: i18n.t('wallet_switcher.copy_address'), subLabel: truncateAddress(account.address),