diff --git a/package.json b/package.json index 9b6dc24fafc..38994b2ae29 100644 --- a/package.json +++ b/package.json @@ -208,7 +208,7 @@ "stream-http": "^3.2.0", "styled-components": "^6.0.7", "uuid": "^9.0.0", - "vaul": "^0.9.0", + "vaul": "^1.1.2", "viem": "^2.10.9", "wagmi": "^2.9.2", "web-vitals": "^2.1.4" diff --git a/src/assets/translations/en/main.json b/src/assets/translations/en/main.json index 1f20ba95db3..854b8574096 100644 --- a/src/assets/translations/en/main.json +++ b/src/assets/translations/en/main.json @@ -11,10 +11,12 @@ "and": "and", "balance": "Balance", "next": "Next", + "edit": "Edit", "error": "Error", "show": "Show", "account": "Account", "address": "Address", + "noThanks": "No Thanks", "done": "Done", "cancel": "Cancel", "close": "Close", @@ -1238,6 +1240,11 @@ "menuItem": "Back up my wallet", "enterPassword": "Enter your password to continue", "testTitle": "Verify Your Secret Recovery Phrase", + "confirm": { + "title": "Backup wallet", + "body": "Before you forget your wallet, would you like to back up your secret recovery phrase?", + "backupNow": "Backup Now" + }, "info": { "title": "Your Secret Recovery Phrase", "description": "Write these 12 words down and store them securely offline. This 12 word phrase is used to recover your wallet private keys.", @@ -1764,12 +1771,15 @@ "error": { "delete": "Unable to delete your wallet, sorry.", "noWallet": "You have no saved wallets", - "pair": "Unable to pair your wallet" + "pair": "Unable to pair your wallet", + "fetchingWallets": "An error occurred while fetching wallets." }, "header": "Saved Wallets", "body": "Loading your saved wallet... Enter your password when prompted.", "button": "Continue", - "confirmForget": "Are you sure you want to forget %{wallet}?" + "forgetWallet": "Forget Wallet", + "confirmForget": "Are you sure you want to forget %{wallet}?", + "confirmForgetBody": "You will not be able to access your wallet if you have not backed it up." }, "import": { "header": "Import your wallet", diff --git a/src/components/MobileWalletDialog/MobileWalletDialog.tsx b/src/components/MobileWalletDialog/MobileWalletDialog.tsx new file mode 100644 index 00000000000..7ad53e41aee --- /dev/null +++ b/src/components/MobileWalletDialog/MobileWalletDialog.tsx @@ -0,0 +1,40 @@ +import { AnimatePresence } from 'framer-motion' +import { MemoryRouter, Redirect, Route, Switch } from 'react-router' +import { Dialog } from 'components/Modal/components/Dialog' + +import { DeleteWallet } from './routes/DeleteWallet/DeleteWallet' +import { RenameWallet } from './routes/RenameWallet' +import { SavedWallets } from './routes/SavedWallets' +import { MobileWalletDialogRoutes } from './types' + +type MobileWalletDialogProps = { + isOpen: boolean + onClose: () => void +} + +export const MobileWalletDialog: React.FC = ({ isOpen, onClose }) => { + return ( + + + + {({ location }) => ( + + + + + + + + + + + + + + + )} + + + + ) +} diff --git a/src/components/MobileWalletDialog/routes/DeleteWallet/Backup.tsx b/src/components/MobileWalletDialog/routes/DeleteWallet/Backup.tsx new file mode 100644 index 00000000000..f4ba223be54 --- /dev/null +++ b/src/components/MobileWalletDialog/routes/DeleteWallet/Backup.tsx @@ -0,0 +1,60 @@ +import { Button, Heading, Stack } from '@chakra-ui/react' +import React, { useCallback } from 'react' +import { useTranslate } from 'react-polyglot' +import { useHistory } from 'react-router' +import { MobileWalletDialogRoutes } from 'components/MobileWalletDialog/types' +import { DialogBackButton } from 'components/Modal/components/DialogBackButton' +import { DialogBody } from 'components/Modal/components/DialogBody' +import { DialogFooter } from 'components/Modal/components/DialogFooter' +import { DialogHeader, DialogHeaderLeft } from 'components/Modal/components/DialogHeader' +import { SlideTransition } from 'components/SlideTransition' +import { RawText } from 'components/Text' +import { useModal } from 'hooks/useModal/useModal' + +// TODO: This is placeholder content follow up PR will implement this correctly + +type BackupProps = { + onBack: () => void +} + +export const Backup: React.FC = ({ onBack }) => { + const translate = useTranslate() + const history = useHistory() + const backupModal = useModal('backupNativePassphrase') + + const handleContinue = useCallback(() => { + history.push(MobileWalletDialogRoutes.ConfirmDelete) + }, [history]) + + const handleBackup = useCallback(() => { + backupModal.open({}) + }, [backupModal]) + + return ( + + + + + + + + + + {translate('modals.shapeShift.backupPassphrase.confirm.title')} + + + {translate('modals.shapeShift.backupPassphrase.confirm.body')} + + + + + + + + + ) +} diff --git a/src/components/MobileWalletDialog/routes/DeleteWallet/Confirm.tsx b/src/components/MobileWalletDialog/routes/DeleteWallet/Confirm.tsx new file mode 100644 index 00000000000..b0ab8a2db18 --- /dev/null +++ b/src/components/MobileWalletDialog/routes/DeleteWallet/Confirm.tsx @@ -0,0 +1,72 @@ +import { WarningIcon } from '@chakra-ui/icons' +import { Alert, AlertDescription, Button, Heading, Stack, Text } from '@chakra-ui/react' +import { useCallback, useState } from 'react' +import { useTranslate } from 'react-polyglot' +import { DialogBackButton } from 'components/Modal/components/DialogBackButton' +import { DialogBody } from 'components/Modal/components/DialogBody' +import { DialogFooter } from 'components/Modal/components/DialogFooter' +import { DialogHeader, DialogHeaderLeft } from 'components/Modal/components/DialogHeader' +import { SlideTransition } from 'components/SlideTransition' +import { deleteWallet } from 'context/WalletProvider/MobileWallet/mobileMessageHandlers' +import type { RevocableWallet } from 'context/WalletProvider/MobileWallet/RevocableWallet' +import { useWallet } from 'hooks/useWallet/useWallet' +import { WalletCard } from 'pages/ConnectWallet/components/WalletCard' + +type ConfirmDeleteProps = { + vault: RevocableWallet + onBack: () => void +} + +export const ConfirmDelete: React.FC = ({ vault, onBack }) => { + const [error, setError] = useState(null) + const translate = useTranslate() + const { disconnect, state } = useWallet() + + const handleDelete = useCallback(async () => { + if (vault?.id) { + try { + await deleteWallet(vault.id) + + if (state.walletInfo?.deviceId === vault.id) { + disconnect() + } + onBack() + } catch (e) { + console.log(e) + setError('walletProvider.shapeShift.load.error.delete') + } + } + }, [onBack, vault?.id, disconnect, state.walletInfo?.deviceId]) + + return ( + + + + + + + + + + + {translate('walletProvider.shapeShift.load.confirmForget', { wallet: vault?.label })} + + + {translate('walletProvider.shapeShift.load.confirmForgetBody')} + + + + + + {error && ( + + {error} + + )} + + + + ) +} diff --git a/src/components/MobileWalletDialog/routes/DeleteWallet/DeleteWallet.tsx b/src/components/MobileWalletDialog/routes/DeleteWallet/DeleteWallet.tsx new file mode 100644 index 00000000000..15ab4001f5f --- /dev/null +++ b/src/components/MobileWalletDialog/routes/DeleteWallet/DeleteWallet.tsx @@ -0,0 +1,61 @@ +import { Button, Heading } from '@chakra-ui/react' +import { AnimatePresence } from 'framer-motion' +import { useCallback } from 'react' +import { useTranslate } from 'react-polyglot' +import { MemoryRouter, Redirect, Route, Switch, useHistory, useLocation } from 'react-router' +import { MobileWalletDialogRoutes } from 'components/MobileWalletDialog/types' +import { DialogBody } from 'components/Modal/components/DialogBody' +import { SlideTransition } from 'components/SlideTransition' +import type { MobileLocationState } from 'context/WalletProvider/MobileWallet/types' + +import { Backup } from './Backup' +import { ConfirmDelete } from './Confirm' + +export const DeleteWallet = () => { + const { + state: { vault }, + } = useLocation() + const history = useHistory() + const translate = useTranslate() + + const handleBack = useCallback(() => { + history.push(MobileWalletDialogRoutes.Saved) + }, [history]) + + if (!vault) + return ( + + + + {translate('common.somethingWentWrong')} + + + + + ) + + return ( + + + + {({ location }) => ( + + + + + + + + + {/* TODO: This will change to backup in a follow up PR */} + + + + )} + + + + ) +} diff --git a/src/components/MobileWalletDialog/routes/RenameWallet.tsx b/src/components/MobileWalletDialog/routes/RenameWallet.tsx new file mode 100644 index 00000000000..11896c7c951 --- /dev/null +++ b/src/components/MobileWalletDialog/routes/RenameWallet.tsx @@ -0,0 +1,99 @@ +import { Button, FormControl, FormErrorMessage, Input } from '@chakra-ui/react' +import { useCallback } from 'react' +import { useForm } from 'react-hook-form' +import { useTranslate } from 'react-polyglot' +import { useHistory, useLocation } from 'react-router' +import { DialogBackButton } from 'components/Modal/components/DialogBackButton' +import { DialogBody } from 'components/Modal/components/DialogBody' +import { DialogFooter } from 'components/Modal/components/DialogFooter' +import { + DialogHeader, + DialogHeaderLeft, + DialogHeaderMiddle, +} from 'components/Modal/components/DialogHeader' +import { DialogTitle } from 'components/Modal/components/DialogTitle' +import { SlideTransition } from 'components/SlideTransition' +import { Text } from 'components/Text' +import { updateWallet } from 'context/WalletProvider/MobileWallet/mobileMessageHandlers' +import type { MobileLocationState } from 'context/WalletProvider/MobileWallet/types' + +import { MobileWalletDialogRoutes } from '../types' + +type FormValues = { + label: string +} + +export const RenameWallet = () => { + const location = useLocation() + const history = useHistory() + const translate = useTranslate() + const { + handleSubmit, + register, + formState: { errors, isSubmitting, isValid }, + } = useForm({ + mode: 'onChange', + defaultValues: { label: location.state.vault?.label }, + }) + + const onSubmit = useCallback( + async (values: FormValues) => { + if (!location.state.vault?.id) return + try { + await updateWallet(location.state.vault.id, { label: values.label }) + history.goBack() + } catch (e) { + console.log(e) + } + }, + [history, location.state.vault?.id], + ) + + const handleBack = useCallback(() => history.push(MobileWalletDialogRoutes.Saved), [history]) + + return ( + + + + + + + {translate('walletProvider.shapeShift.rename.header')} + + +
+ + + + + {errors.label && errors.label.message} + + + + + +
+
+ ) +} diff --git a/src/components/MobileWalletDialog/routes/SavedWallets.tsx b/src/components/MobileWalletDialog/routes/SavedWallets.tsx new file mode 100644 index 00000000000..678152ba6ec --- /dev/null +++ b/src/components/MobileWalletDialog/routes/SavedWallets.tsx @@ -0,0 +1,126 @@ +import { ChatIcon, SettingsIcon } from '@chakra-ui/icons' +import { Button, Stack } from '@chakra-ui/react' +import { WalletConnectToDappsHeaderButton } from 'plugins/walletConnectToDapps/components/header/WalletConnectToDappsHeaderButton' +import { useCallback, useMemo, useState } from 'react' +import { TbCircleArrowDown, TbCirclePlus } from 'react-icons/tb' +import { useTranslate } from 'react-polyglot' +import { MainNavLink } from 'components/Layout/Header/NavBar/MainNavLink' +import { DialogBody } from 'components/Modal/components/DialogBody' +import { DialogFooter } from 'components/Modal/components/DialogFooter' +import { + DialogHeader, + DialogHeaderMiddle, + DialogHeaderRight, +} from 'components/Modal/components/DialogHeader' +import { DialogTitle } from 'components/Modal/components/DialogTitle' +import { SlideTransition } from 'components/SlideTransition' +import { WalletActions } from 'context/WalletProvider/actions' +import { KeyManager } from 'context/WalletProvider/KeyManager' +import { useFeatureFlag } from 'hooks/useFeatureFlag/useFeatureFlag' +import { useModal } from 'hooks/useModal/useModal' +import { useToggle } from 'hooks/useToggle/useToggle' +import { useWallet } from 'hooks/useWallet/useWallet' +import { MobileWallestList } from 'pages/ConnectWallet/components/WalletList' + +const addIcon = +const importIcon = +const settingsIcon = +const chatIcon = + +type SavedWalletsProps = { + onClose: () => void +} + +export const SavedWallets: React.FC = ({ onClose }) => { + const translate = useTranslate() + const settings = useModal('settings') + const feedbackSupport = useModal('feedbackSupport') + const isWalletConnectToDappsV2Enabled = useFeatureFlag('WalletConnectToDappsV2') + const { dispatch, create, importWallet } = useWallet() + const [isEditing, toggleEditing] = useToggle() + const [error, setError] = useState(null) + const handleClickSettings = useCallback(() => { + settings.open({}) + onClose() + }, [onClose, settings]) + + const handleClickSupport = useCallback(() => { + feedbackSupport.open({}) + onClose() + }, [onClose, feedbackSupport]) + + const handleCreate = useCallback(() => { + dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: true }) + create(KeyManager.Mobile) + }, [create, dispatch]) + + const handleImport = useCallback(() => { + dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: true }) + importWallet(KeyManager.Mobile) + }, [dispatch, importWallet]) + + const mobileWalletFooter = useMemo(() => { + return ( + + + + + ) + }, [handleCreate, handleImport, translate]) + + return ( + + + + {translate('walletProvider.shapeShift.load.header')} + + + {!error ? ( + + ) : null} + + + + + + + {isWalletConnectToDappsV2Enabled && } + + + + + ) +} diff --git a/src/components/MobileWalletDialog/types.ts b/src/components/MobileWalletDialog/types.ts new file mode 100644 index 00000000000..bd4493ff270 --- /dev/null +++ b/src/components/MobileWalletDialog/types.ts @@ -0,0 +1,7 @@ +export enum MobileWalletDialogRoutes { + Saved = '/saved', + Rename = '/rename', + Delete = '/delete', + Backup = '/delete/backup', + ConfirmDelete = '/delete/confirm', +} diff --git a/src/components/Modal/components/Dialog.tsx b/src/components/Modal/components/Dialog.tsx index 3505e9b9c09..07cd8ad2f47 100644 --- a/src/components/Modal/components/Dialog.tsx +++ b/src/components/Modal/components/Dialog.tsx @@ -1,7 +1,7 @@ -import { Modal, ModalContent, ModalOverlay, useMediaQuery } from '@chakra-ui/react' +import { Box, Modal, ModalContent, ModalOverlay, useMediaQuery } from '@chakra-ui/react' import styled from '@emotion/styled' import type { PropsWithChildren } from 'react' -import React, { useEffect, useMemo, useState } from 'react' +import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react' import { Drawer } from 'vaul' import { useDialog, withDialogProvider } from 'context/DialogContextProvider/DialogContextProvider' import { isMobile } from 'lib/globals' @@ -12,6 +12,7 @@ export type DialogProps = { onClose: () => void height?: string isFullScreen?: boolean + isDisablingPropagation?: boolean } & PropsWithChildren const CustomDrawerContent = styled(Drawer.Content)` @@ -40,6 +41,7 @@ const DialogWindow: React.FC = ({ height, isFullScreen, children, + isDisablingPropagation = true, }) => { const [isLargerThanMd] = useMediaQuery(`(min-width: ${breakpoints['md']})`, { ssr: false }) const { snapPoint, setIsOpen, isOpen: isDialogOpen } = useDialog() @@ -58,7 +60,7 @@ const DialogWindow: React.FC = ({ const contentStyle = useMemo(() => { return { maxHeight: isFullScreen ? '100vh' : 'calc(100% - env(safe-area-inset-top))', - height: isFullScreen ? viewportHeight : height, + height: isFullScreen ? viewportHeight : height || '80vh', paddingTop: isFullScreen ? 'env(safe-area-inset-top)' : 0, } }, [height, isFullScreen, viewportHeight]) @@ -67,11 +69,62 @@ const DialogWindow: React.FC = ({ setIsOpen(isOpen) }, [isOpen, setIsOpen]) + // This is a workaround to prevent the body to be pointer-events: none when the dialog is open if isDisablingPropagation is false + useEffect(() => { + if (!isDialogOpen) return + if (isDisablingPropagation) return + + const originalPointerEvents = document.body.style.pointerEvents + const focusGuardedElements = document.querySelectorAll('*[data-radix-focus-guard]') + + const raf = window.requestAnimationFrame(() => { + document.body.style.pointerEvents = 'auto' + focusGuardedElements.forEach(element => { + element.style.pointerEvents = 'auto' + }) + }) + + return () => { + window.cancelAnimationFrame(raf) + document.body.style.pointerEvents = originalPointerEvents + focusGuardedElements.forEach(element => { + element.style.pointerEvents = 'inherit' + }) + } + }, [isDialogOpen, isDisablingPropagation]) + + // If we stack multiple modals and drawers on mobile then we shouldn't trap focus + useLayoutEffect(() => { + if (!isMobile || isLargerThanMd) return + + document.addEventListener('focusin', e => e.stopImmediatePropagation()) + document.addEventListener('focusout', e => e.stopImmediatePropagation()) + + return () => { + document.removeEventListener('focusin', e => e.stopImmediatePropagation()) + document.removeEventListener('focusout', e => e.stopImmediatePropagation()) + } + }, [isLargerThanMd]) + if (isMobile || !isLargerThanMd) { return ( - + - + {!isDisablingPropagation ? ( + + ) : null} + {isDisablingPropagation ? : null} {children} diff --git a/src/components/QrCodeScanner/QrCodeScanner.tsx b/src/components/QrCodeScanner/QrCodeScanner.tsx index f60f8c99375..3ab0c9f55d0 100644 --- a/src/components/QrCodeScanner/QrCodeScanner.tsx +++ b/src/components/QrCodeScanner/QrCodeScanner.tsx @@ -25,7 +25,12 @@ const isPermissionError = ( ): error is DOMException['message'] => typeof (error as DOMException['message']) === 'string' && error === PERMISSION_ERROR -const boxStyle = { width: '100%', minHeight: '298px', overflow: 'hidden', borderRadius: '1rem' } +const boxStyle = { + width: '100%', + minHeight: '298px', + overflow: 'hidden', + borderRadius: '1rem', +} const qrBoxStyle = { width: 250, height: 250 } const arrowBackIcon = @@ -89,7 +94,7 @@ export const QrCodeScanner = ({ {error ? ( - + { const [testCount, setTestCount] = useState(0) const shuffledNumbers = useMemo(() => slice(shuffle(range(12)), 0, TEST_COUNT_REQUIRED), []) const revoker = useMemo(() => new (Revocable(class {}))(), []) + const queryClient = useQueryClient() const { vault } = location.state @@ -99,6 +101,8 @@ export const MobileCreateTest = ({ history, location }: MobileSetupProps) => { ;(async () => { if (vault?.label && vault?.mnemonic) { const newWallet = await addWallet({ label: vault.label, mnemonic: vault.mnemonic }) + + queryClient.invalidateQueries({ queryKey: ['listWallets'] }) history.replace('/mobile/success', { vault: newWallet! }) } })() @@ -108,7 +112,7 @@ export const MobileCreateTest = ({ history, location }: MobileSetupProps) => { setTimeout(() => revoker.revoke(), 250) } } - }, [testCount, history, vault, revoker]) + }, [testCount, history, vault, revoker, queryClient]) const handleClick = useCallback( (index: number) => { diff --git a/src/context/WalletProvider/MobileWallet/components/MobileImport.tsx b/src/context/WalletProvider/MobileWallet/components/MobileImport.tsx index 901b5bbf007..0a84bb21797 100644 --- a/src/context/WalletProvider/MobileWallet/components/MobileImport.tsx +++ b/src/context/WalletProvider/MobileWallet/components/MobileImport.tsx @@ -7,6 +7,7 @@ import { ModalHeader, Textarea, } from '@chakra-ui/react' +import { useQueryClient } from '@tanstack/react-query' import * as bip39 from 'bip39' import { useCallback, useMemo } from 'react' import { useForm } from 'react-hook-form' @@ -25,6 +26,7 @@ export const MobileImport = ({ history }: RouteComponentProps) => { register, formState: { errors, isSubmitting }, } = useForm({ shouldUnregister: true }) + const queryClient = useQueryClient() const onSubmit = useCallback( async (values: FormValues) => { @@ -35,12 +37,13 @@ export const MobileImport = ({ history }: RouteComponentProps) => { label: values.name.trim(), }) history.push('/mobile/success', { vault }) + queryClient.invalidateQueries({ queryKey: ['listWallets'] }) } catch (e) { console.log(e) setError('mnemonic', { type: 'manual', message: 'walletProvider.shapeShift.import.header' }) } }, - [history, setError], + [history, queryClient, setError], ) const translate = useTranslate() diff --git a/src/context/WalletProvider/WalletViewsSwitch.tsx b/src/context/WalletProvider/WalletViewsSwitch.tsx index 65a2566231a..d0d93c55d90 100644 --- a/src/context/WalletProvider/WalletViewsSwitch.tsx +++ b/src/context/WalletProvider/WalletViewsSwitch.tsx @@ -15,6 +15,7 @@ import { Route, Switch, useHistory, useLocation, useRouteMatch } from 'react-rou import { SlideTransition } from 'components/SlideTransition' import { WalletActions } from 'context/WalletProvider/actions' import { useWallet } from 'hooks/useWallet/useWallet' +import { isMobile } from 'lib/globals' import { SUPPORTED_WALLETS } from './config' import { KeyManager } from './KeyManager' @@ -82,6 +83,12 @@ export const WalletViewsSwitch = () => { ]) const handleBack = useCallback(async () => { + if (initialRoute === history.location.pathname && isMobile) { + onClose() + + return + } + history.goBack() // If we're back at the select wallet modal, remove the initial route // otherwise clicking the button for the same wallet doesn't do anything @@ -89,8 +96,9 @@ export const WalletViewsSwitch = () => { if ([INITIAL_WALLET_MODAL_ROUTE, NativeWalletRoutes.Load].includes(pathname)) { dispatch({ type: WalletActions.SET_INITIAL_ROUTE, payload: '' }) } + await cancelWalletRequests() - }, [cancelWalletRequests, dispatch, history]) + }, [dispatch, history, initialRoute, onClose, cancelWalletRequests]) useEffect(() => { if (initialRoute) { diff --git a/src/pages/ConnectWallet/MobileConnect.tsx b/src/pages/ConnectWallet/MobileConnect.tsx index df0eab2cd75..35e13fdc161 100644 --- a/src/pages/ConnectWallet/MobileConnect.tsx +++ b/src/pages/ConnectWallet/MobileConnect.tsx @@ -27,14 +27,12 @@ import { SlideTransitionY } from 'components/SlideTransitionY' import { RawText, Text } from 'components/Text' import { WalletActions } from 'context/WalletProvider/actions' import { KeyManager } from 'context/WalletProvider/KeyManager' -import { useLocalWallet } from 'context/WalletProvider/local-wallet' -import { MobileConfig } from 'context/WalletProvider/MobileWallet/config' -import { getWallet, listWallets } from 'context/WalletProvider/MobileWallet/mobileMessageHandlers' +import { listWallets } from 'context/WalletProvider/MobileWallet/mobileMessageHandlers' import type { RevocableWallet } from 'context/WalletProvider/MobileWallet/RevocableWallet' import { useQuery } from 'hooks/useQuery/useQuery' import { useWallet } from 'hooks/useWallet/useWallet' -import { WalletCard } from './components/WalletCard' +import { MobileWallestList } from './components/WalletList' const containerStyles = { touchAction: 'none' } @@ -63,8 +61,7 @@ const BodyText: React.FC = props => ( ) export const MobileConnect = () => { - const { create, importWallet, dispatch, getAdapter, state } = useWallet() - const localWallet = useLocalWallet() + const { create, importWallet, dispatch, state } = useWallet() const translate = useTranslate() const [wallets, setWallets] = useState([]) const [error, setError] = useState(null) @@ -126,56 +123,6 @@ export const MobileConnect = () => { } }, [wallets]) - const handleWalletSelect = useCallback( - async (item: RevocableWallet) => { - const adapter = await getAdapter(KeyManager.Mobile) - const deviceId = item?.id - if (adapter && deviceId) { - const { name, icon } = MobileConfig - try { - const revoker = await getWallet(deviceId) - if (!revoker?.mnemonic) throw new Error(`Mobile wallet not found: ${deviceId}`) - if (!revoker?.id) throw new Error(`Revoker ID not found: ${deviceId}`) - - const wallet = await adapter.pairDevice(revoker.id) - await wallet?.loadDevice({ mnemonic: revoker.mnemonic }) - if (!(await wallet?.isInitialized())) { - await wallet?.initialize() - } - dispatch({ - type: WalletActions.SET_WALLET, - payload: { - wallet, - name, - icon, - deviceId, - meta: { label: item.label }, - connectedType: KeyManager.Mobile, - }, - }) - dispatch({ - type: WalletActions.SET_IS_CONNECTED, - payload: true, - }) - dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: false }) - dispatch({ - type: WalletActions.SET_CONNECTOR_TYPE, - payload: { modalType: KeyManager.Mobile, isMipdProvider: false }, - }) - - localWallet.setLocalWallet({ type: KeyManager.Mobile, deviceId }) - localWallet.setLocalNativeWalletName(item?.label ?? 'label') - } catch (e) { - console.log(e) - setError('walletProvider.shapeShift.load.error.pair') - } - } else { - setError('walletProvider.shapeShift.load.error.pair') - } - }, - [dispatch, getAdapter, localWallet], - ) - const handleToggleWallets = useCallback(() => { setHideWallets(!hideWallets) // allow users with saved wallets to toggle between saved and create/import }, [hideWallets]) @@ -237,14 +184,7 @@ export const MobileConnect = () => { {translate('connectWalletPage.mobileSelectBody')} - {wallets.map(wallet => ( - - ))} + @@ -260,16 +200,7 @@ export const MobileConnect = () => { ) - }, [ - error, - handleCreate, - handleImport, - handleToggleWallets, - handleWalletSelect, - hideWallets, - translate, - wallets, - ]) + }, [error, handleCreate, handleImport, handleToggleWallets, hideWallets, translate, wallets]) return ( +const editIcon = +const deleteIcon = + type WalletCardProps = { id?: string wallet: RevocableWallet - onClick: (arg: RevocableWallet) => void + onClick?: (arg: RevocableWallet) => Promise + isActive?: boolean + isEditing?: boolean + onRename?: (wallet: RevocableWallet) => void + onDelete?: (wallet: RevocableWallet) => void + _active?: ButtonProps['_active'] + _hover?: ButtonProps['_hover'] } -export const WalletCard: React.FC = ({ id, wallet, onClick }) => { +export const WalletCard: React.FC = ({ + id, + wallet, + onClick, + isActive, + isEditing, + onRename, + onDelete, + _active, + _hover, +}) => { + const translate = useTranslate() const profileImage = useMemo(() => { if (!id) return '' return makeBlockiesUrl(`${id}ifyoudriveatruckdriveitlikeyouhaveafarm`) }, [id]) + const avatar = useMemo( () => , [profileImage], ) const handleClick = useCallback(() => { - onClick(wallet) + onClick && onClick(wallet) }, [onClick, wallet]) + + const handleRename = useCallback(() => onRename && onRename(wallet), [onRename, wallet]) + const handleDelete = useCallback(() => onDelete && onDelete(wallet), [onDelete, wallet]) + + const rightElement = useMemo(() => { + if (isEditing) { + return ( + + + + + ) + } + if (isActive) { + return activeIcon + } + }, [handleDelete, handleRename, isActive, isEditing, translate]) return ( - ) } diff --git a/src/pages/ConnectWallet/components/WalletList.tsx b/src/pages/ConnectWallet/components/WalletList.tsx new file mode 100644 index 00000000000..0574657519c --- /dev/null +++ b/src/pages/ConnectWallet/components/WalletList.tsx @@ -0,0 +1,177 @@ +import { Alert, AlertDescription, AlertIcon, Center, Spinner, Stack } from '@chakra-ui/react' +import { useQuery } from '@tanstack/react-query' +import { useCallback, useEffect, useMemo, useState } from 'react' +import { useTranslate } from 'react-polyglot' +import { useHistory } from 'react-router' +import { MobileWalletDialogRoutes } from 'components/MobileWalletDialog/types' +import { WalletActions } from 'context/WalletProvider/actions' +import { KeyManager } from 'context/WalletProvider/KeyManager' +import { useLocalWallet } from 'context/WalletProvider/local-wallet' +import { MobileConfig } from 'context/WalletProvider/MobileWallet/config' +import { getWallet, listWallets } from 'context/WalletProvider/MobileWallet/mobileMessageHandlers' +import type { RevocableWallet } from 'context/WalletProvider/MobileWallet/RevocableWallet' +import { useWallet } from 'hooks/useWallet/useWallet' + +import { WalletCard } from './WalletCard' + +type MobileWalletDialogProps = { + footerComponent?: JSX.Element + isEditing?: boolean + onErrorChange?: (error: string | null) => void +} + +export const MobileWallestList: React.FC = ({ + footerComponent, + isEditing, + onErrorChange, +}) => { + const { dispatch, getAdapter, state } = useWallet() + const { walletInfo } = state + const localWallet = useLocalWallet() + const history = useHistory() + const [error, setError] = useState(null) + const translate = useTranslate() + + const { isLoading, data: wallets } = useQuery({ + queryKey: ['listWallets'], + staleTime: 0, + gcTime: 0, + refetchOnMount: true, + queryFn: async () => { + try { + const vaults = await listWallets() + if (!vaults.length) { + setError('walletProvider.shapeShift.load.error.noWallet') + } else { + return vaults + } + } catch (e) { + console.log(e) + setError('walletProvider.shapeShift.load.error.fetchingWallets') + } + }, + }) + + const handleWalletSelect = useCallback( + async (item: RevocableWallet) => { + const adapter = await getAdapter(KeyManager.Mobile) + const deviceId = item?.id + if (adapter && deviceId) { + const { name, icon } = MobileConfig + try { + const revoker = await getWallet(deviceId) + if (!revoker?.mnemonic) throw new Error(`Mobile wallet not found: ${deviceId}`) + if (!revoker?.id) throw new Error(`Revoker ID not found: ${deviceId}`) + + const wallet = await adapter.pairDevice(revoker.id) + await wallet?.loadDevice({ mnemonic: revoker.mnemonic }) + if (!(await wallet?.isInitialized())) { + await wallet?.initialize() + } + dispatch({ + type: WalletActions.SET_WALLET, + payload: { + wallet, + name, + icon, + deviceId, + meta: { label: item.label }, + connectedType: KeyManager.Mobile, + }, + }) + dispatch({ type: WalletActions.SET_IS_CONNECTED, payload: true }) + dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: false }) + dispatch({ + type: WalletActions.SET_CONNECTOR_TYPE, + payload: { modalType: KeyManager.Mobile, isMipdProvider: false }, + }) + + localWallet.setLocalWallet({ type: KeyManager.Mobile, deviceId }) + localWallet.setLocalNativeWalletName(item?.label ?? 'label') + } catch (e) { + console.log(e) + setError('walletProvider.shapeShift.load.error.pair') + } + } else { + setError('walletProvider.shapeShift.load.error.pair') + } + }, + [dispatch, getAdapter, localWallet], + ) + + const handleRename = useCallback( + (wallet: RevocableWallet) => { + history.push(MobileWalletDialogRoutes.Rename, { vault: wallet }) + }, + [history], + ) + + const handleDelete = useCallback( + (wallet: RevocableWallet) => { + history.push(MobileWalletDialogRoutes.Delete, { vault: wallet }) + }, + [history], + ) + + useEffect(() => { + onErrorChange?.(error) + }, [error, onErrorChange]) + + const content = useMemo(() => { + if (error) { + return ( + + + {translate(error)} + + ) + } + return ( + + {wallets?.map(wallet => { + const isSelected = walletInfo?.deviceId === wallet.id + const _hover = isSelected + ? { bg: 'background.button.secondary.base', opacity: '1' } + : undefined + const _active = isSelected + ? { bg: 'background.button.secondary.base', opacity: '1' } + : undefined + return ( + + ) + })} + + ) + }, [ + error, + handleDelete, + handleRename, + handleWalletSelect, + isEditing, + translate, + walletInfo?.deviceId, + wallets, + ]) + + return isLoading ? ( +
+ +
+ ) : ( + + {content} + {footerComponent} + + ) +} diff --git a/src/pages/Dashboard/components/DashboardHeader/DashboardHeaderTop.tsx b/src/pages/Dashboard/components/DashboardHeader/DashboardHeaderTop.tsx index 0ed64614145..31836a2bffd 100644 --- a/src/pages/Dashboard/components/DashboardHeader/DashboardHeaderTop.tsx +++ b/src/pages/Dashboard/components/DashboardHeader/DashboardHeaderTop.tsx @@ -3,12 +3,15 @@ import { Button, Container, Flex, IconButton, useDisclosure } from '@chakra-ui/r import type { ResponsiveValue } from '@chakra-ui/system' import type { Property } from 'csstype' import { memo, useCallback } from 'react' +import { FiLogOut } from 'react-icons/fi' import { IoEllipsisHorizontal, IoSwapVerticalSharp } from 'react-icons/io5' import { useTranslate } from 'react-polyglot' import { QRCodeIcon } from 'components/Icons/QRCode' +import { MobileWalletDialog } from 'components/MobileWalletDialog/MobileWalletDialog' import { useBrowserRouter } from 'hooks/useBrowserRouter/useBrowserRouter' import { useModal } from 'hooks/useModal/useModal' import { useWallet } from 'hooks/useWallet/useWallet' +import { isMobile } from 'lib/globals' import { EditAvatarButton, ProfileAvatar } from '../ProfileAvatar/ProfileAvatar' import { DashboardDrawer } from './DashboardDrawer' @@ -18,7 +21,7 @@ const qrCodeIcon = const arrowUpIcon = const arrowDownIcon = const ioSwapVerticalSharpIcon = -const moreIcon = +const moreIcon = isMobile ? : const ButtonRowDisplay = { base: 'flex', md: 'none' } @@ -110,7 +113,11 @@ export const DashboardHeaderTop = memo(() => { >
- + {isMobile ? ( + + ) : ( + + )} ) }) diff --git a/src/plugins/walletConnectToDapps/components/modals/connect/Connect.tsx b/src/plugins/walletConnectToDapps/components/modals/connect/Connect.tsx index fce01bb83f3..d4df1f09a35 100644 --- a/src/plugins/walletConnectToDapps/components/modals/connect/Connect.tsx +++ b/src/plugins/walletConnectToDapps/components/modals/connect/Connect.tsx @@ -1,9 +1,11 @@ -import { Modal, ModalCloseButton, ModalContent, ModalOverlay, useToast } from '@chakra-ui/react' +import { useToast } from '@chakra-ui/react' import { captureException } from '@sentry/react' import { ConnectContent } from 'plugins/walletConnectToDapps/components/modals/connect/ConnectContent' import { useWalletConnectV2 } from 'plugins/walletConnectToDapps/WalletConnectV2Provider' import { useCallback } from 'react' import { useTranslate } from 'react-polyglot' +import { Dialog } from 'components/Modal/components/Dialog' +import { DialogBody } from 'components/Modal/components/DialogBody' import { getMixPanel } from 'lib/mixpanel/mixPanelSingleton' import { MixPanelEvent } from 'lib/mixpanel/types' @@ -14,7 +16,6 @@ type Props = { } const borderRadiusProp = { base: 0, md: 'xl' } -const minWidthProp = { base: '100%', md: '500px' } const maxWidthProp = { base: 'full', md: '500px' } const Connect = ({ initialUri, isOpen, onClose }: Props) => { @@ -52,20 +53,17 @@ const Connect = ({ initialUri, isOpen, onClose }: Props) => { ) return ( - - - + - - - +
+ ) } diff --git a/src/plugins/walletConnectToDapps/components/modals/connect/ConnectContent.tsx b/src/plugins/walletConnectToDapps/components/modals/connect/ConnectContent.tsx index 88735f8e611..672592696ed 100644 --- a/src/plugins/walletConnectToDapps/components/modals/connect/ConnectContent.tsx +++ b/src/plugins/walletConnectToDapps/components/modals/connect/ConnectContent.tsx @@ -19,6 +19,13 @@ import { useForm, useWatch } from 'react-hook-form' import { useTranslate } from 'react-polyglot' import { QRCodeIcon } from 'components/Icons/QRCode' import { WalletConnectIcon } from 'components/Icons/WalletConnectIcon' +import { DialogCloseButton } from 'components/Modal/components/DialogCloseButton' +import { + DialogHeader, + DialogHeaderLeft, + DialogHeaderMiddle, + DialogHeaderRight, +} from 'components/Modal/components/DialogHeader' import { QrCodeScanner } from 'components/QrCodeScanner/QrCodeScanner' import { Text } from 'components/Text' @@ -76,61 +83,70 @@ export const ConnectContent: React.FC = ({ } return ( - -
- - - - - - - - + <> + + + + + + + + + + + + + + + + + - - - - + + + + + - - - - {formState.errors?.uri?.message} - - - - {translate('plugins.walletConnectToDapps.modal.connect.disclaimerBody')} - - - - - - + + {formState.errors?.uri?.message} + + + + {translate('plugins.walletConnectToDapps.modal.connect.disclaimerBody')} + + + +
+ +
+ ) } diff --git a/yarn.lock b/yarn.lock index 79a6a155ad0..ee8983a972e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3503,15 +3503,6 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.2.0": - version: 7.24.0 - resolution: "@babel/runtime@npm:7.24.0" - dependencies: - regenerator-runtime: ^0.14.0 - checksum: 7a6a5d40fbdd68491ec183ba2e631c07415119960083b4fd76564cce3751e9acd2f12ab89575e38496fa389fa06d458732776e69ee1858e366cc3fbdb049f847 - languageName: node - linkType: hard - "@babel/runtime@npm:^7.18.3": version: 7.22.6 resolution: "@babel/runtime@npm:7.22.6" @@ -3530,6 +3521,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.2.0": + version: 7.24.0 + resolution: "@babel/runtime@npm:7.24.0" + dependencies: + regenerator-runtime: ^0.14.0 + checksum: 7a6a5d40fbdd68491ec183ba2e631c07415119960083b4fd76564cce3751e9acd2f12ab89575e38496fa389fa06d458732776e69ee1858e366cc3fbdb049f847 + languageName: node + linkType: hard + "@babel/runtime@npm:^7.25.0": version: 7.25.0 resolution: "@babel/runtime@npm:7.25.0" @@ -10866,291 +10866,270 @@ __metadata: languageName: node linkType: hard -"@radix-ui/primitive@npm:1.0.1": - version: 1.0.1 - resolution: "@radix-ui/primitive@npm:1.0.1" - dependencies: - "@babel/runtime": ^7.13.10 - checksum: 2b93e161d3fdabe9a64919def7fa3ceaecf2848341e9211520c401181c9eaebb8451c630b066fad2256e5c639c95edc41de0ba59c40eff37e799918d019822d1 +"@radix-ui/primitive@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/primitive@npm:1.1.1" + checksum: d7e819177590108b74139809d52ec043c0962ae3513e947998be575fb13639c5c1c091896ddcf1d6a22a777d44ade59d22c2019ce9099607fc62a5de09c59707 languageName: node linkType: hard -"@radix-ui/react-compose-refs@npm:1.0.1": - version: 1.0.1 - resolution: "@radix-ui/react-compose-refs@npm:1.0.1" - dependencies: - "@babel/runtime": ^7.13.10 +"@radix-ui/react-compose-refs@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-compose-refs@npm:1.1.1" peerDependencies: "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true - checksum: 2b9a613b6db5bff8865588b6bf4065f73021b3d16c0a90b2d4c23deceeb63612f1f15de188227ebdc5f88222cab031be617a9dd025874c0487b303be3e5cc2a8 + checksum: 1be82f9f7fab96cc10f167a2e4f976e0135a63d473334f664c06f02af13bc5ea1994cb0505f89ed190d756cb65d57506721c030908af07e49b9e3cfd36044f33 languageName: node linkType: hard -"@radix-ui/react-context@npm:1.0.1": - version: 1.0.1 - resolution: "@radix-ui/react-context@npm:1.0.1" - dependencies: - "@babel/runtime": ^7.13.10 +"@radix-ui/react-context@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-context@npm:1.1.1" peerDependencies: "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true - checksum: 60e9b81d364f40c91a6213ec953f7c64fcd9d75721205a494a5815b3e5ae0719193429b62ee6c7002cd6aaf70f8c0e2f08bdbaba9ffcc233044d32b56d2127d1 + checksum: 9a04db236685dacc2f5ab2bdcfc4c82b974998e712ab97d79b11d5b4ef073d24aa9392398c876ef6cb3c59f40299285ceee3646187ad818cdad4fe1c74469d3f languageName: node linkType: hard -"@radix-ui/react-dialog@npm:^1.0.4": - version: 1.0.5 - resolution: "@radix-ui/react-dialog@npm:1.0.5" - dependencies: - "@babel/runtime": ^7.13.10 - "@radix-ui/primitive": 1.0.1 - "@radix-ui/react-compose-refs": 1.0.1 - "@radix-ui/react-context": 1.0.1 - "@radix-ui/react-dismissable-layer": 1.0.5 - "@radix-ui/react-focus-guards": 1.0.1 - "@radix-ui/react-focus-scope": 1.0.4 - "@radix-ui/react-id": 1.0.1 - "@radix-ui/react-portal": 1.0.4 - "@radix-ui/react-presence": 1.0.1 - "@radix-ui/react-primitive": 1.0.3 - "@radix-ui/react-slot": 1.0.2 - "@radix-ui/react-use-controllable-state": 1.0.1 - aria-hidden: ^1.1.1 - react-remove-scroll: 2.5.5 +"@radix-ui/react-dialog@npm:^1.1.1": + version: 1.1.5 + resolution: "@radix-ui/react-dialog@npm:1.1.5" + dependencies: + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-dismissable-layer": 1.1.4 + "@radix-ui/react-focus-guards": 1.1.1 + "@radix-ui/react-focus-scope": 1.1.1 + "@radix-ui/react-id": 1.1.0 + "@radix-ui/react-portal": 1.1.3 + "@radix-ui/react-presence": 1.1.2 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-slot": 1.1.1 + "@radix-ui/react-use-controllable-state": 1.1.0 + aria-hidden: ^1.2.4 + react-remove-scroll: ^2.6.2 peerDependencies: "@types/react": "*" "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true "@types/react-dom": optional: true - checksum: 3d11ca31afb794a6dd286005ab7894cb0ce7bc2de5481de98900470b11d495256401306763de030f5e35aa545ff90d34632ffd54a1b29bf55afba813be4bb84a + checksum: 0897fd319e9566fac87141ae74b91dee17202c5ef68850ce0f15702bfb7bd45dbacec0221b3e0a1ec25e43f79727ff88948098eb83e072c655215129dac72bc8 languageName: node linkType: hard -"@radix-ui/react-dismissable-layer@npm:1.0.5": - version: 1.0.5 - resolution: "@radix-ui/react-dismissable-layer@npm:1.0.5" +"@radix-ui/react-dismissable-layer@npm:1.1.4": + version: 1.1.4 + resolution: "@radix-ui/react-dismissable-layer@npm:1.1.4" dependencies: - "@babel/runtime": ^7.13.10 - "@radix-ui/primitive": 1.0.1 - "@radix-ui/react-compose-refs": 1.0.1 - "@radix-ui/react-primitive": 1.0.3 - "@radix-ui/react-use-callback-ref": 1.0.1 - "@radix-ui/react-use-escape-keydown": 1.0.3 + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-callback-ref": 1.1.0 + "@radix-ui/react-use-escape-keydown": 1.1.0 peerDependencies: "@types/react": "*" "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true "@types/react-dom": optional: true - checksum: e73cf4bd3763f4d55b1bea7486a9700384d7d94dc00b1d5a75e222b2f1e4f32bc667a206ca4ed3baaaf7424dce7a239afd0ba59a6f0d89c3462c4e6e8d029a04 + checksum: 387060b412c8db474d2e0395c5ad8eb93544a81268be2a22ff1c9c8127f9477471bd8a6d026cfc9a541512223edbf3107f6603d574b46b554431819c3c68ff2e languageName: node linkType: hard -"@radix-ui/react-focus-guards@npm:1.0.1": - version: 1.0.1 - resolution: "@radix-ui/react-focus-guards@npm:1.0.1" - dependencies: - "@babel/runtime": ^7.13.10 +"@radix-ui/react-focus-guards@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-focus-guards@npm:1.1.1" peerDependencies: "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true - checksum: 1f8ca8f83b884b3612788d0742f3f054e327856d90a39841a47897dbed95e114ee512362ae314177de226d05310047cabbf66b686ae86ad1b65b6b295be24ef7 + checksum: ac8dd31f48fa0500bafd9368f2f06c5a06918dccefa89fa5dc77ca218dc931a094a81ca57f6b181138029822f7acdd5280dceccf5ba4d9263c754fb8f7961879 languageName: node linkType: hard -"@radix-ui/react-focus-scope@npm:1.0.4": - version: 1.0.4 - resolution: "@radix-ui/react-focus-scope@npm:1.0.4" +"@radix-ui/react-focus-scope@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-focus-scope@npm:1.1.1" dependencies: - "@babel/runtime": ^7.13.10 - "@radix-ui/react-compose-refs": 1.0.1 - "@radix-ui/react-primitive": 1.0.3 - "@radix-ui/react-use-callback-ref": 1.0.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-callback-ref": 1.1.0 peerDependencies: "@types/react": "*" "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true "@types/react-dom": optional: true - checksum: 3481db1a641513a572734f0bcb0e47fefeba7bccd6ec8dde19f520719c783ef0b05a55ef0d5292078ed051cc5eda46b698d5d768da02e26e836022f46b376fd1 + checksum: 8716fe9b029a66f81b37e4e22457dd0fc7b4dba573d712454e18ead850f256d84cd994eeebcc31dd7780cf1028b6410d9ebe152fff4478d3b4ce2700690a38f4 languageName: node linkType: hard -"@radix-ui/react-id@npm:1.0.1": - version: 1.0.1 - resolution: "@radix-ui/react-id@npm:1.0.1" +"@radix-ui/react-id@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/react-id@npm:1.1.0" dependencies: - "@babel/runtime": ^7.13.10 - "@radix-ui/react-use-layout-effect": 1.0.1 + "@radix-ui/react-use-layout-effect": 1.1.0 peerDependencies: "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true - checksum: 446a453d799cc790dd2a1583ff8328da88271bff64530b5a17c102fa7fb35eece3cf8985359d416f65e330cd81aa7b8fe984ea125fc4f4eaf4b3801d698e49fe + checksum: 6fbc9d1739b3b082412da10359e63967b4f3a60383ebda4c9e56b07a722d29bee53b203b3b1418f88854a29315a7715867133bb149e6e22a027a048cdd20d970 languageName: node linkType: hard -"@radix-ui/react-portal@npm:1.0.4": - version: 1.0.4 - resolution: "@radix-ui/react-portal@npm:1.0.4" +"@radix-ui/react-portal@npm:1.1.3": + version: 1.1.3 + resolution: "@radix-ui/react-portal@npm:1.1.3" dependencies: - "@babel/runtime": ^7.13.10 - "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-layout-effect": 1.1.0 peerDependencies: "@types/react": "*" "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true "@types/react-dom": optional: true - checksum: c4cf35e2f26a89703189d0eef3ceeeb706ae0832e98e558730a5e929ca7c72c7cb510413a24eca94c7732f8d659a1e81942bec7b90540cb73ce9e4885d040b64 + checksum: 470fb50c940772d05cc268e219b3d15848909dcd0a2dc1952965d0af905992f0ccab99e99c490dea6564c441397eba720b8425ba9f4582c94bef40ebe27ac0d0 languageName: node linkType: hard -"@radix-ui/react-presence@npm:1.0.1": - version: 1.0.1 - resolution: "@radix-ui/react-presence@npm:1.0.1" +"@radix-ui/react-presence@npm:1.1.2": + version: 1.1.2 + resolution: "@radix-ui/react-presence@npm:1.1.2" dependencies: - "@babel/runtime": ^7.13.10 - "@radix-ui/react-compose-refs": 1.0.1 - "@radix-ui/react-use-layout-effect": 1.0.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-use-layout-effect": 1.1.0 peerDependencies: "@types/react": "*" "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true "@types/react-dom": optional: true - checksum: ed2ff9faf9e4257a4065034d3771459e5a91c2d840b2fcec94661761704dbcb65bcdd927d28177a2a129b3dab5664eb90a9b88309afe0257a9f8ba99338c0d95 + checksum: 0345bc8d3e1ddcbf4b864025833c71f3d76e4801ce16ad126a98aed816be6e819c4fe01097c6c1320771b947f5a14929cc610d18e7a1438cfb5573289fa4d4a6 languageName: node linkType: hard -"@radix-ui/react-primitive@npm:1.0.3": - version: 1.0.3 - resolution: "@radix-ui/react-primitive@npm:1.0.3" +"@radix-ui/react-primitive@npm:2.0.1": + version: 2.0.1 + resolution: "@radix-ui/react-primitive@npm:2.0.1" dependencies: - "@babel/runtime": ^7.13.10 - "@radix-ui/react-slot": 1.0.2 + "@radix-ui/react-slot": 1.1.1 peerDependencies: "@types/react": "*" "@types/react-dom": "*" - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true "@types/react-dom": optional: true - checksum: 9402bc22923c8e5c479051974a721c301535c36521c0237b83e5fa213d013174e77f3ad7905e6d60ef07e14f88ec7f4ea69891dc7a2b39047f8d3640e8f8d713 + checksum: d75882209101155f20babcff9475b887929db6473cd8e5b56d0c24d24d0042202e0fa785e6d6c6b322a96d9777cd0ef7610def9e11ea69839c6b204f1c99cf16 languageName: node linkType: hard -"@radix-ui/react-slot@npm:1.0.2": - version: 1.0.2 - resolution: "@radix-ui/react-slot@npm:1.0.2" +"@radix-ui/react-slot@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-slot@npm:1.1.1" dependencies: - "@babel/runtime": ^7.13.10 - "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-compose-refs": 1.1.1 peerDependencies: "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true - checksum: edf5edf435ff594bea7e198bf16d46caf81b6fb559493acad4fa8c308218896136acb16f9b7238c788fd13e94a904f2fd0b6d834e530e4cae94522cdb8f77ce9 + checksum: ac391b921dcde1a71db8307247b36cd6908e0886d7a7b0babeb25158292bc29b61ccfb3f83279bfad11fe1f0f90e3e2f3de93b1174f36d107d77b073fe1a652a languageName: node linkType: hard -"@radix-ui/react-use-callback-ref@npm:1.0.1": - version: 1.0.1 - resolution: "@radix-ui/react-use-callback-ref@npm:1.0.1" - dependencies: - "@babel/runtime": ^7.13.10 +"@radix-ui/react-use-callback-ref@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/react-use-callback-ref@npm:1.1.0" peerDependencies: "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true - checksum: b9fd39911c3644bbda14a84e4fca080682bef84212b8d8931fcaa2d2814465de242c4cfd8d7afb3020646bead9c5e539d478cea0a7031bee8a8a3bb164f3bc4c + checksum: 2ec7903c67e3034b646005556f44fd975dc5204db6885fc58403e3584f27d95f0b573bc161de3d14fab9fda25150bf3b91f718d299fdfc701c736bd0bd2281fa languageName: node linkType: hard -"@radix-ui/react-use-controllable-state@npm:1.0.1": - version: 1.0.1 - resolution: "@radix-ui/react-use-controllable-state@npm:1.0.1" +"@radix-ui/react-use-controllable-state@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/react-use-controllable-state@npm:1.1.0" dependencies: - "@babel/runtime": ^7.13.10 - "@radix-ui/react-use-callback-ref": 1.0.1 + "@radix-ui/react-use-callback-ref": 1.1.0 peerDependencies: "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true - checksum: dee2be1937d293c3a492cb6d279fc11495a8f19dc595cdbfe24b434e917302f9ac91db24e8cc5af9a065f3f209c3423115b5442e65a5be9fd1e9091338972be9 + checksum: a6c167cf8eb0744effbeab1f92ea6c0ad71838b222670c0488599f28eecd941d87ac1eed4b5d3b10df6dc7b7b2edb88a54e99d92c2942ce3b21f81d5c188f32d languageName: node linkType: hard -"@radix-ui/react-use-escape-keydown@npm:1.0.3": - version: 1.0.3 - resolution: "@radix-ui/react-use-escape-keydown@npm:1.0.3" +"@radix-ui/react-use-escape-keydown@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/react-use-escape-keydown@npm:1.1.0" dependencies: - "@babel/runtime": ^7.13.10 - "@radix-ui/react-use-callback-ref": 1.0.1 + "@radix-ui/react-use-callback-ref": 1.1.0 peerDependencies: "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true - checksum: c6ed0d9ce780f67f924980eb305af1f6cce2a8acbaf043a58abe0aa3cc551d9aa76ccee14531df89bbee302ead7ecc7fce330886f82d4672c5eda52f357ef9b8 + checksum: 9bf88ea272b32ea0f292afd336780a59c5646f795036b7e6105df2d224d73c54399ee5265f61d571eb545d28382491a8b02dc436e3088de8dae415d58b959b71 languageName: node linkType: hard -"@radix-ui/react-use-layout-effect@npm:1.0.1": - version: 1.0.1 - resolution: "@radix-ui/react-use-layout-effect@npm:1.0.1" - dependencies: - "@babel/runtime": ^7.13.10 +"@radix-ui/react-use-layout-effect@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/react-use-layout-effect@npm:1.1.0" peerDependencies: "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true - checksum: bed9c7e8de243a5ec3b93bb6a5860950b0dba359b6680c84d57c7a655e123dec9b5891c5dfe81ab970652e7779fe2ad102a23177c7896dde95f7340817d47ae5 + checksum: 271ea0bf1cd74718895a68414a6e95537737f36e02ad08eeb61a82b229d6abda9cff3135a479e134e1f0ce2c3ff97bb85babbdce751985fb755a39b231d7ccf2 languageName: node linkType: hard @@ -12518,7 +12497,7 @@ __metadata: typescript: ^5.2.2 uuid: ^9.0.0 val-loader: ^4.0.0 - vaul: ^0.9.0 + vaul: ^1.1.2 viem: ^2.10.9 vite-tsconfig-paths: ^4.2.3 vitest: 1.1.1 @@ -17612,16 +17591,7 @@ __metadata: languageName: node linkType: hard -"aria-hidden@npm:^1.1.1": - version: 1.2.3 - resolution: "aria-hidden@npm:1.2.3" - dependencies: - tslib: ^2.0.0 - checksum: 7d7d211629eef315e94ed3b064c6823d13617e609d3f9afab1c2ed86399bb8e90405f9bdd358a85506802766f3ecb468af985c67c846045a34b973bcc0289db9 - languageName: node - linkType: hard - -"aria-hidden@npm:^1.2.3": +"aria-hidden@npm:^1.2.3, aria-hidden@npm:^1.2.4": version: 1.2.4 resolution: "aria-hidden@npm:1.2.4" dependencies: @@ -34455,9 +34425,9 @@ pvutils@latest: languageName: node linkType: hard -"react-remove-scroll-bar@npm:^2.3.3": - version: 2.3.5 - resolution: "react-remove-scroll-bar@npm:2.3.5" +"react-remove-scroll-bar@npm:^2.3.6": + version: 2.3.6 + resolution: "react-remove-scroll-bar@npm:2.3.6" dependencies: react-style-singleton: ^2.2.1 tslib: ^2.0.0 @@ -34467,31 +34437,31 @@ pvutils@latest: peerDependenciesMeta: "@types/react": optional: true - checksum: 0b6eee6d338085f0c766dc6d780100041a39377bc1a2a1b99a13444832b91885fc632b7521636a29d26710cf8bb0f9f4177123abe088a358597ac0f0e8e42f45 + checksum: e793fe110e2ea60d5724d0b60f09de1f6cd1b080df00df9e68bb9a1b985895830e703194647059fdc22402a67a89b7673a5260773b89bcd98031fd99bc91aefa languageName: node linkType: hard -"react-remove-scroll-bar@npm:^2.3.6": - version: 2.3.6 - resolution: "react-remove-scroll-bar@npm:2.3.6" +"react-remove-scroll-bar@npm:^2.3.7": + version: 2.3.8 + resolution: "react-remove-scroll-bar@npm:2.3.8" dependencies: - react-style-singleton: ^2.2.1 + react-style-singleton: ^2.2.2 tslib: ^2.0.0 peerDependencies: - "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: "@types/react": optional: true - checksum: e793fe110e2ea60d5724d0b60f09de1f6cd1b080df00df9e68bb9a1b985895830e703194647059fdc22402a67a89b7673a5260773b89bcd98031fd99bc91aefa + checksum: c4663247f689dbe51c370836edf735487f6d8796acb7f15b09e8a1c14e84c7997360e8e3d54de2bc9c0e782fed2b2c4127d15b4053e4d2cf26839e809e57605f languageName: node linkType: hard -"react-remove-scroll@npm:2.5.5": - version: 2.5.5 - resolution: "react-remove-scroll@npm:2.5.5" +"react-remove-scroll@npm:^2.5.6": + version: 2.5.10 + resolution: "react-remove-scroll@npm:2.5.10" dependencies: - react-remove-scroll-bar: ^2.3.3 + react-remove-scroll-bar: ^2.3.6 react-style-singleton: ^2.2.1 tslib: ^2.1.0 use-callback-ref: ^1.3.0 @@ -34502,26 +34472,26 @@ pvutils@latest: peerDependenciesMeta: "@types/react": optional: true - checksum: 2c7fe9cbd766f5e54beb4bec2e2efb2de3583037b23fef8fa511ab426ed7f1ae992382db5acd8ab5bfb030a4b93a06a2ebca41377d6eeaf0e6791bb0a59616a4 + checksum: 2a926718b93e783732a2b369fec1349beaa187e8110ff6fa41940668a2826e59de1ee617dfc2bb5bbf5ad3a186d06b388baf56f9f0553fcac76cf9cec4b83eb0 languageName: node linkType: hard -"react-remove-scroll@npm:^2.5.6": - version: 2.5.10 - resolution: "react-remove-scroll@npm:2.5.10" +"react-remove-scroll@npm:^2.6.2": + version: 2.6.3 + resolution: "react-remove-scroll@npm:2.6.3" dependencies: - react-remove-scroll-bar: ^2.3.6 - react-style-singleton: ^2.2.1 + react-remove-scroll-bar: ^2.3.7 + react-style-singleton: ^2.2.3 tslib: ^2.1.0 - use-callback-ref: ^1.3.0 - use-sidecar: ^1.1.2 + use-callback-ref: ^1.3.3 + use-sidecar: ^1.1.3 peerDependencies: - "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: "@types/react": optional: true - checksum: 2a926718b93e783732a2b369fec1349beaa187e8110ff6fa41940668a2826e59de1ee617dfc2bb5bbf5ad3a186d06b388baf56f9f0553fcac76cf9cec4b83eb0 + checksum: a4afd320435cc25a6ee39d7cef2f605dca14cc7618e1cdab24ed0924fa71d8c3756626334dedc9a578945d7ba6f8f87d7b8b66b48034853dc4dbfbda0a1b228b languageName: node linkType: hard @@ -34723,6 +34693,22 @@ pvutils@latest: languageName: node linkType: hard +"react-style-singleton@npm:^2.2.2, react-style-singleton@npm:^2.2.3": + version: 2.2.3 + resolution: "react-style-singleton@npm:2.2.3" + dependencies: + get-nonce: ^1.0.0 + tslib: ^2.0.0 + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: a7b0bf493c9231065ebafa84c4237aed997c746c561196121b7de82fe155a5355b372db5070a3ac9fe980cf7f60dc0f1e8cf6402a2aa5b2957392932ccf76e76 + languageName: node + linkType: hard + "react-swipeable-views-core@npm:^0.14.0": version: 0.14.0 resolution: "react-swipeable-views-core@npm:0.14.0" @@ -39651,6 +39637,21 @@ pvutils@latest: languageName: node linkType: hard +"use-callback-ref@npm:^1.3.3": + version: 1.3.3 + resolution: "use-callback-ref@npm:1.3.3" + dependencies: + tslib: ^2.0.0 + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 4da1c82d7a2409cee6c882748a40f4a083decf238308bf12c3d0166f0e338f8d512f37b8d11987eb5a421f14b9b5b991edf3e11ed25c3bb7a6559081f8359b44 + languageName: node + linkType: hard + "use-isomorphic-layout-effect@npm:^1.0.0": version: 1.1.2 resolution: "use-isomorphic-layout-effect@npm:1.1.2" @@ -39679,6 +39680,22 @@ pvutils@latest: languageName: node linkType: hard +"use-sidecar@npm:^1.1.3": + version: 1.1.3 + resolution: "use-sidecar@npm:1.1.3" + dependencies: + detect-node-es: ^1.1.0 + tslib: ^2.0.0 + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 88664c6b2c5b6e53e4d5d987694c9053cea806da43130248c74ca058945c8caa6ccb7b1787205a9eb5b9d124633e42153848904002828acabccdc48cda026622 + languageName: node + linkType: hard + "use-subscription@npm:^1.3.0": version: 1.8.0 resolution: "use-subscription@npm:1.8.0" @@ -39981,15 +39998,15 @@ pvutils@latest: languageName: node linkType: hard -"vaul@npm:^0.9.0": - version: 0.9.0 - resolution: "vaul@npm:0.9.0" +"vaul@npm:^1.1.2": + version: 1.1.2 + resolution: "vaul@npm:1.1.2" dependencies: - "@radix-ui/react-dialog": ^1.0.4 + "@radix-ui/react-dialog": ^1.1.1 peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - checksum: 12c4ced1a91400abe8d20c0c2108f2ffe7d81172eb08c3d7a24e9f813a034e00a9ddb43bb30a7ec312e71292d7b15b13d0fd9e866646b35e13b6d8bc61974198 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + checksum: e8f72775f066318daab44701e77c88c99f2b179f196093a32a57cf91e39b1fc3258e608c92359eee39ce47604298c1da68db1d11e19cb485507c28138d9f27ea languageName: node linkType: hard