Skip to content

Commit

Permalink
feat: new mobile wallet drawer import flow (#8810)
Browse files Browse the repository at this point in the history
  • Loading branch information
NeOMakinG authored Feb 13, 2025
1 parent 341c1ed commit 170a733
Show file tree
Hide file tree
Showing 11 changed files with 480 additions and 136 deletions.
6 changes: 5 additions & 1 deletion src/components/MobileWalletDialog/MobileWalletDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Dialog } from 'components/Modal/components/Dialog'

import { CreateWalletRouter } from './routes/CreateWallet/CreateWalletRouter'
import { DeleteWallet } from './routes/DeleteWallet/DeleteWallet'
import { ImportRouter } from './routes/ImportWallet/ImportRouter'
import { ManualBackup } from './routes/ManualBackup/ManualBackup'
import { RenameWallet } from './routes/RenameWallet'
import { SavedWallets } from './routes/SavedWallets'
Expand All @@ -30,6 +31,9 @@ export const MobileWalletDialog: React.FC<MobileWalletDialogProps> = ({
<Route path={MobileWalletDialogRoutes.Backup}>
<ManualBackup showContinueButton={false} />
</Route>
<Route path={MobileWalletDialogRoutes.Import}>
<ImportRouter onClose={onClose} defaultRoute={defaultRoute} />
</Route>
<Route path={MobileWalletDialogRoutes.Saved}>
<SavedWallets onClose={onClose} />
</Route>
Expand All @@ -40,7 +44,7 @@ export const MobileWalletDialog: React.FC<MobileWalletDialogProps> = ({
<DeleteWallet />
</Route>
<Route path={MobileWalletDialogRoutes.Create}>
<CreateWalletRouter onClose={onClose} />
<CreateWalletRouter onClose={onClose} defaultRoute={defaultRoute} />
</Route>
<Redirect exact from='/' to={defaultRoute} />
</Switch>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,4 @@
import {
Box,
Button,
Flex,
Icon,
Spinner,
Text as CText,
useColorModeValue,
VStack,
} from '@chakra-ui/react'
import { useQueryClient } from '@tanstack/react-query'
import { Box, Button, Flex, Icon, Text as CText, useColorModeValue, VStack } from '@chakra-ui/react'
import * as bip39 from 'bip39'
import { uniq } from 'lodash'
import { useCallback, useEffect, useMemo, useState } from 'react'
Expand All @@ -27,16 +17,7 @@ import {
DialogHeaderRight,
} from 'components/Modal/components/DialogHeader'
import { SlideTransition } from 'components/SlideTransition'
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 { addWallet } from 'context/WalletProvider/MobileWallet/mobileMessageHandlers'
import type { RevocableWallet } from 'context/WalletProvider/MobileWallet/RevocableWallet'
import type { MobileLocationState } from 'context/WalletProvider/MobileWallet/types'
import { useWallet } from 'hooks/useWallet/useWallet'
import { preferences } from 'state/slices/preferencesSlice/preferencesSlice'
import { useAppDispatch } from 'state/store'

import { MobileWalletDialogRoutes } from '../../types'

Expand All @@ -53,12 +34,6 @@ export const CreateBackupConfirm = () => {
const borderColor = useColorModeValue('gray.100', 'gray.700')
const [selectedWordIndex, setSelectedWordIndex] = useState<number | null>(null)
const [testWords, setTestWords] = useState<string[]>([])
const queryClient = useQueryClient()
const { dispatch, getAdapter } = useWallet()
const localWallet = useLocalWallet()
const [isLoading, setIsLoading] = useState(false)
const appDispatch = useAppDispatch()
const { setWelcomeModal } = preferences.actions

const backgroundDottedSx = useMemo(
() => ({
Expand Down Expand Up @@ -99,52 +74,6 @@ export const CreateBackupConfirm = () => {
return allWords.sort(() => Math.random() - 0.5)
}, [])

const handleWalletSelect = useCallback(
async (wallet: RevocableWallet) => {
const adapter = await getAdapter(KeyManager.Mobile)
const deviceId = wallet.id
if (adapter && deviceId) {
const { name, icon } = MobileConfig
try {
const wallet = await adapter.pairDevice(deviceId)
await wallet?.loadDevice({ mnemonic: location.state?.vault?.mnemonic ?? '' })

if (!(await wallet?.isInitialized())) {
await wallet?.initialize()
}
dispatch({
type: WalletActions.SET_WALLET,
payload: {
wallet,
name,
icon,
deviceId,
meta: { label: location.state?.vault?.label },
connectedType: KeyManager.Mobile,
},
})
dispatch({ type: WalletActions.SET_IS_CONNECTED, payload: true })
dispatch({
type: WalletActions.SET_CONNECTOR_TYPE,
payload: { modalType: KeyManager.Mobile, isMipdProvider: false },
})

localWallet.setLocalWallet({ type: KeyManager.Mobile, deviceId })
localWallet.setLocalNativeWalletName(location.state?.vault?.label ?? 'label')
} catch (e) {
console.log(e)
}
}
},
[
dispatch,
getAdapter,
localWallet,
location.state?.vault?.mnemonic,
location.state?.vault?.label,
],
)

useEffect(() => {
if (selectedWordIndex === null && words.length > 0) {
setSelectedWordIndex(0)
Expand All @@ -153,40 +82,14 @@ export const CreateBackupConfirm = () => {
}
}, [selectedWordIndex, words, randomWordIndices, generateTestWords])

const saveAndSelectWallet = useCallback(async () => {
setIsLoading(true)
if (location.state?.vault?.label && location.state?.vault?.mnemonic) {
const wallet = await addWallet({
label: location.state.vault.label,
mnemonic: location.state.vault.mnemonic,
})

if (!wallet) {
setIsLoading(false)
return
}

appDispatch(setWelcomeModal({ show: true }))
await handleWalletSelect(wallet)
await queryClient.invalidateQueries({ queryKey: ['listWallets'] })
history.push(MobileWalletDialogRoutes.CreateBackupSuccess)
wallet.revoke()
}
}, [
location.state?.vault,
handleWalletSelect,
queryClient,
history,
appDispatch,
setWelcomeModal,
])

const handleWordClick = useCallback(
(word: string) => {
const currentWordIndex = randomWordIndices[selectedWordIndex ?? 0]
if (words[currentWordIndex] === word) {
if ((selectedWordIndex ?? 0) + 1 >= TEST_COUNT_REQUIRED) {
saveAndSelectWallet()
history.push(MobileWalletDialogRoutes.CreateBackupSuccess, {
vault: location.state?.vault,
})
return
}

Expand All @@ -201,24 +104,20 @@ export const CreateBackupConfirm = () => {
setTestWords(generateTestWords(targetWord ?? ''))
}
},
[randomWordIndices, selectedWordIndex, words, generateTestWords, saveAndSelectWallet],
[
randomWordIndices,
selectedWordIndex,
words,
generateTestWords,
history,
location.state?.vault,
],
)

const handleBack = useCallback(() => {
history.push(MobileWalletDialogRoutes.CreateBackup, { vault: location.state?.vault })
}, [history, location.state?.vault])

if (isLoading) {
return (
<SlideTransition>
<Flex direction='column' align='center' justify='center' height='100%' p={8}>
<Spinner size='xl' mb={4} />
<CText>{translate('common.loading')}</CText>
</Flex>
</SlideTransition>
)
}

return (
<SlideTransition>
<DialogHeader>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,112 @@
import { Button, Icon, Text, VStack } from '@chakra-ui/react'
import { useQueryClient } from '@tanstack/react-query'
import { useCallback } from 'react'
import { IoIosCheckmarkCircle } from 'react-icons/io'
import { useTranslate } from 'react-polyglot'
import { useLocation } from 'react-router'
import { DialogBody } from 'components/Modal/components/DialogBody'
import { DialogCloseButton } from 'components/Modal/components/DialogCloseButton'
import { DialogFooter } from 'components/Modal/components/DialogFooter'
import { DialogHeader, DialogHeaderRight } from 'components/Modal/components/DialogHeader'
import { SlideTransition } from 'components/SlideTransition'
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 { addWallet } from 'context/WalletProvider/MobileWallet/mobileMessageHandlers'
import type { MobileLocationState } from 'context/WalletProvider/MobileWallet/types'
import { useWallet } from 'hooks/useWallet/useWallet'
import { preferences } from 'state/slices/preferencesSlice/preferencesSlice'
import { useAppDispatch } from 'state/store'

type CreateSuccessProps = {
onClose: () => void
}

export const CreateSuccess = ({ onClose }: CreateSuccessProps) => {
const translate = useTranslate()
const appDispatch = useAppDispatch()
const { setWelcomeModal } = preferences.actions
const location = useLocation<MobileLocationState>()
const queryClient = useQueryClient()
const { dispatch, getAdapter } = useWallet()
const localWallet = useLocalWallet()

const saveAndSelectWallet = useCallback(async () => {
if (location.state?.vault?.label && location.state?.vault?.mnemonic) {
const wallet = await addWallet({
label: location.state.vault.label,
mnemonic: location.state.vault.mnemonic,
})

if (!wallet) {
return
}

const adapter = await getAdapter(KeyManager.Mobile)
const deviceId = wallet.id
if (adapter && deviceId) {
const { name, icon } = MobileConfig
try {
const walletInstance = await adapter.pairDevice(deviceId)
await walletInstance?.loadDevice({ mnemonic: location.state?.vault?.mnemonic ?? '' })

if (!(await walletInstance?.isInitialized())) {
await walletInstance?.initialize()
}
dispatch({
type: WalletActions.SET_WALLET,
payload: {
wallet: walletInstance,
name,
icon,
deviceId,
meta: { label: location.state?.vault?.label },
connectedType: KeyManager.Mobile,
},
})
dispatch({ type: WalletActions.SET_IS_CONNECTED, payload: true })
dispatch({
type: WalletActions.SET_CONNECTOR_TYPE,
payload: { modalType: KeyManager.Mobile, isMipdProvider: false },
})

localWallet.setLocalWallet({ type: KeyManager.Mobile, deviceId })
localWallet.setLocalNativeWalletName(location.state?.vault?.label ?? 'label')
} catch (e) {
console.log(e)
}
}
await queryClient.invalidateQueries({ queryKey: ['listWallets'] })
wallet.revoke()
appDispatch(setWelcomeModal({ show: true }))
}
}, [
location.state?.vault,
dispatch,
getAdapter,
localWallet,
queryClient,
appDispatch,
setWelcomeModal,
])

const handleViewWallet = useCallback(() => {
saveAndSelectWallet()
onClose()
}, [onClose, saveAndSelectWallet])

const handleClose = useCallback(() => {
saveAndSelectWallet()
onClose()
}, [onClose])
appDispatch(setWelcomeModal({ show: true }))
}, [onClose, appDispatch, setWelcomeModal, saveAndSelectWallet])

return (
<SlideTransition>
<DialogHeader>
<DialogHeaderRight>
<DialogCloseButton />
<DialogCloseButton onClick={handleClose} />
</DialogHeaderRight>
</DialogHeader>
<DialogBody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,17 @@ type FormValues = {
label: string
}

export const CreateWallet = () => {
type CreateWalletProps = {
isDefaultRoute?: boolean
onClose: () => void
handleRedirectToHome: () => void
}

export const CreateWallet = ({
isDefaultRoute,
onClose,
handleRedirectToHome,
}: CreateWalletProps) => {
const location = useLocation<MobileLocationState | undefined>()
const history = useHistory()
const translate = useTranslate()
Expand Down Expand Up @@ -62,7 +72,13 @@ export const CreateWallet = () => {
[history, vault],
)

const handleBack = useCallback(() => history.push(MobileWalletDialogRoutes.Saved), [history])
const handleBack = useCallback(() => {
if (isDefaultRoute) {
onClose()
} else {
handleRedirectToHome()
}
}, [handleRedirectToHome, isDefaultRoute, onClose])

useEffect(() => {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AnimatePresence } from 'framer-motion'
import { MemoryRouter, Redirect, Route, Switch } from 'react-router'
import { useCallback } from 'react'
import { MemoryRouter, Redirect, Route, Switch, useHistory } from 'react-router'
import { MobileWalletDialogRoutes } from 'components/MobileWalletDialog/types'
import { SlideTransition } from 'components/SlideTransition'

Expand All @@ -11,9 +12,16 @@ import { KeepSafe } from './KeepSafe'

type CreateWalletRouterProps = {
onClose: () => void
defaultRoute: MobileWalletDialogRoutes
}

export const CreateWalletRouter = ({ onClose }: CreateWalletRouterProps) => {
export const CreateWalletRouter = ({ onClose, defaultRoute }: CreateWalletRouterProps) => {
const history = useHistory()

const handleRedirectToHome = useCallback(() => {
history.push(MobileWalletDialogRoutes.Saved)
}, [history])

return (
<SlideTransition>
<MemoryRouter>
Expand All @@ -34,7 +42,11 @@ export const CreateWalletRouter = ({ onClose }: CreateWalletRouterProps) => {
<KeepSafe />
</Route>
<Route path={MobileWalletDialogRoutes.Create}>
<CreateWallet />
<CreateWallet
isDefaultRoute={defaultRoute === MobileWalletDialogRoutes.Create}
onClose={onClose}
handleRedirectToHome={handleRedirectToHome}
/>
</Route>
<Redirect from='/' to={MobileWalletDialogRoutes.Create} />
</Switch>
Expand Down
Loading

0 comments on commit 170a733

Please sign in to comment.