Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: new mobile wallet drawer import flow #8810

Merged
merged 6 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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