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 wallet flow for native #8597

Merged
merged 37 commits into from
Jan 17, 2025
Merged
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
1133808
[skip ci] wip: new wallet flow for native
gomesalexandre Jan 15, 2025
6da6baa
[skip ci] feat: handlers
gomesalexandre Jan 15, 2025
d7b51a1
feat: stopping point
gomesalexandre Jan 16, 2025
0797e59
Merge branch 'develop' into feat_new_wallet_flow_native
gomesalexandre Jan 16, 2025
01142ec
[skip ci] chore: one too deep on the cleanup
gomesalexandre Jan 16, 2025
40ec15c
feat: progression
gomesalexandre Jan 16, 2025
5485c88
fix: routing shenanigans
gomesalexandre Jan 16, 2025
1f3882b
fix: more routing shenanigans
gomesalexandre Jan 16, 2025
f2ce7ab
feat: missing routes
gomesalexandre Jan 16, 2025
718dc3a
fix: styles
gomesalexandre Jan 16, 2025
8110223
fix: more style fixes
gomesalexandre Jan 16, 2025
4c83aa9
[skip ci] wip: more wire more up
gomesalexandre Jan 16, 2025
27cf208
fix: routing
gomesalexandre Jan 16, 2025
9a1ea6a
feat: rm disgusting iiafe
gomesalexandre Jan 16, 2025
956f522
chore: trigger CI
gomesalexandre Jan 16, 2025
b8a2e8a
fix: lint
gomesalexandre Jan 16, 2025
c910f7f
fix: light mode things
gomesalexandre Jan 16, 2025
6d0a12e
feat: less aggressive light mode
gomesalexandre Jan 16, 2025
489ecb7
fix: mobile things
gomesalexandre Jan 16, 2025
ae6dc60
fix: lint
gomesalexandre Jan 16, 2025
5838748
feat: progress
gomesalexandre Jan 16, 2025
b95c538
fix: tests with flag on
gomesalexandre Jan 16, 2025
2e8d382
feat: more progress more ion
gomesalexandre Jan 16, 2025
17adcab
feat: add divider
gomesalexandre Jan 16, 2025
6830ce4
fix: scrolly bits
gomesalexandre Jan 16, 2025
f60cafc
feat: truncate long-ass wallet names
gomesalexandre Jan 16, 2025
50cfde3
feat: slightly adjust styles
gomesalexandre Jan 16, 2025
e945fcb
feat: almost there
gomesalexandre Jan 16, 2025
d9f739c
Merge branch 'develop' into feat_new_wallet_flow_native
gomesalexandre Jan 16, 2025
381c8a2
feat: here we fuarking go
gomesalexandre Jan 16, 2025
1604511
feat: cleanup
gomesalexandre Jan 16, 2025
84191d7
feat: more cleanup
gomesalexandre Jan 16, 2025
c46c1ee
feat: more cleanup
gomesalexandre Jan 16, 2025
7848539
feat: last cleanup i swear
gomesalexandre Jan 16, 2025
f7b4b16
Revert "feat: last cleanup i swear"
gomesalexandre Jan 16, 2025
52ae673
Merge branch 'develop' into feat_new_wallet_flow_native
NeOMakinG Jan 17, 2025
3234de0
feat: extract hdwalletNativeVaultsList queryKey / queryFn
gomesalexandre Jan 17, 2025
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
Prev Previous commit
Next Next commit
feat: stopping point
gomesalexandre committed Jan 16, 2025
commit d7b51a1256e3cee7bf9333f66322e8cbdd9495bb
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -17,12 +17,16 @@ import type { FieldValues } from 'react-hook-form'
import { useForm } from 'react-hook-form'
import { FaEye, FaEyeSlash } from 'react-icons/fa'
import { useTranslate } from 'react-polyglot'
import type { RouteComponentProps } from 'react-router-dom'
import { Text } from 'components/Text'
import { NativeWalletRoutes } from 'context/WalletProvider/types'

import type { NativeSetupProps, NativeWalletValues } from '../types'
import type { LocationState, NativeWalletValues } from '../types'

export const NativePassword = ({ history, location }: NativeSetupProps) => {
export const NativePassword = ({
history,
location,
}: RouteComponentProps<{}, any, LocationState>) => {
const translate = useTranslate()
const [showPw, setShowPw] = useState<boolean>(false)
const [showConfirmPw, setShowConfirmPw] = useState<boolean>(false)
Original file line number Diff line number Diff line change
@@ -14,11 +14,12 @@ import type { FieldValues } from 'react-hook-form'
import { useForm } from 'react-hook-form'
import { FaEye, FaEyeSlash } from 'react-icons/fa'
import { useTranslate } from 'react-polyglot'
import type { RouteComponentProps } from 'react-router-dom'
import { Text } from 'components/Text'

import type { NativeSetupProps, NativeWalletValues } from '../types'
import type { NativeWalletValues } from '../types'

export const NativeRename = ({ history, location }: NativeSetupProps) => {
export const NativeRename = ({ history, location }: RouteComponentProps) => {
const translate = useTranslate()
const [showPw, setShowPw] = useState<boolean>(false)

Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Box, ModalBody, ModalHeader } from '@chakra-ui/react'
import type { RouteComponentProps } from 'react-router-dom'
import { Text } from 'components/Text'

import { useNativeSuccess } from '../hooks/useNativeSuccess'
import type { NativeSetupProps } from '../types'
import type { LocationState } from '../types'

export const NativeSuccess = ({ location }: NativeSetupProps) => {
export const NativeSuccess = ({ location }: RouteComponentProps<{}, any, LocationState>) => {
const { isSuccessful } = useNativeSuccess({ vault: location.state.vault })

return (
Original file line number Diff line number Diff line change
@@ -7,9 +7,10 @@ import slice from 'lodash/slice'
import uniq from 'lodash/uniq'
import { useCallback, useEffect, useState } from 'react'
import { useTranslate } from 'react-polyglot'
import type { RouteComponentProps } from 'react-router-dom'
import { RawText, Text } from 'components/Text'

import type { NativeSetupProps } from '../types'
import type { LocationState } from '../types'

const Revocable = Default.Revocable
const revocable = Default.revocable
@@ -26,7 +27,10 @@ type TestState = {
correctAnswerIndex: number
}

export const NativeTestPhrase = ({ history, location }: NativeSetupProps) => {
export const NativeTestPhrase = ({
history,
location,
}: RouteComponentProps<{}, any, LocationState>) => {
const translate = useTranslate()
const [testState, setTestState] = useState<TestState | null>(null)
const [hasAlreadySaved, setHasAlreadySaved] = useState(false)
13 changes: 0 additions & 13 deletions src/context/WalletProvider/NativeWallet/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import type { Vault } from '@shapeshiftoss/hdwallet-native-vault'
import type React from 'react'
import type { RouteComponentProps } from 'react-router-dom'
import type { ActionTypes } from 'context/WalletProvider/actions'

export type NativeWalletValues = {
name: string
@@ -21,13 +18,3 @@ export interface LocationState {
message: string
}
}

export interface NativeSetupProps
extends RouteComponentProps<
{},
any, // history
LocationState
> {
vault: Vault
dispatch: React.Dispatch<ActionTypes>
}
Original file line number Diff line number Diff line change
@@ -13,15 +13,23 @@ import {
import { useCallback, useEffect, useMemo, useState } from 'react'
import { isMobile } from 'react-device-detect'
import { useTranslate } from 'react-polyglot'
import { Route, Switch, useHistory } from 'react-router-dom'
import type { RouteComponentProps } from 'react-router-dom'
import { Route, Switch, useHistory, useLocation } from 'react-router-dom'
import { Text } from 'components/Text'
import { WalletActions } from 'context/WalletProvider/actions'
import { useWallet } from 'hooks/useWallet/useWallet'

import { SnapInstall } from '../MetaMask/components/SnapInstall'
import { SnapUpdate } from '../MetaMask/components/SnapUpdate'
import { EnterPassword } from '../NativeWallet/components/EnterPassword'
import { NativeCreate } from '../NativeWallet/components/NativeCreate'
import { NativeImportKeystore } from '../NativeWallet/components/NativeImportKeystore'
import { NativePassword } from '../NativeWallet/components/NativePassword'
import { NativeSuccess } from '../NativeWallet/components/NativeSuccess'
import type { LocationState } from '../NativeWallet/types'
import { NativeWalletRoutes } from '../types'
import { InstalledWalletsSection } from './sections/InstalledWalletsSection'
import { SavedWalletsSection } from './sections/SavedWalletsSection'
import { MipdBody } from './wallets/mipd/MipdBody'
import { NativeStart } from './wallets/native/NativeStart'

@@ -51,6 +59,54 @@ const RightPanelContent = ({
state: { modalType, isMipdProvider },
} = useWallet()

const location = useLocation()

if (location.pathname.startsWith('/native')) {
return (
<Switch>
<Route
exact
path={NativeWalletRoutes.ImportKeystore}
// we need to pass an arg here, so we need an anonymous function wrapper
// eslint-disable-next-line react-memo/require-usememo
render={routeProps => <NativeImportKeystore {...routeProps} />}
/>
<Route exact path={NativeWalletRoutes.Create}>
<NativeCreate />
</Route>
<Route
exact
path={NativeWalletRoutes.Password}
// TODO(gomes): add NativePassowrdNew with new design
// we need to pass an arg here, so we need an anonymous function wrapper
// eslint-disable-next-line react-memo/require-usememo
render={routeProps => (
<NativePassword {...(routeProps as RouteComponentProps<{}, any, LocationState>)} />
)}
/>
<Route
exact
path={NativeWalletRoutes.EnterPassword}
// TODO(gomes): add EnterPasswordNew with new design
>
<EnterPassword />
</Route>

<Route
exact
path={NativeWalletRoutes.Success}
// TODO(gomes): add NativeSuccessNew with new design
// we need to pass an arg here, so we need an anonymous function wrapper
// eslint-disable-next-line react-memo/require-usememo
render={routeProps => (
<NativeSuccess {...(routeProps as RouteComponentProps<{}, any, LocationState>)} />
)}
/>
</Switch>
)
}

// No modal type, and no in-flight native routes - assume enpty state
if (!modalType) return <NativeStart />

if (isMipdProvider && modalType) {
@@ -175,6 +231,7 @@ export const NewWalletViewsSwitch = () => {
() => (
<Box w={sectionsWidth} p={6}>
<Text translation='common.connectWallet' fontSize='xl' fontWeight='semibold' />
<SavedWalletsSection />
<InstalledWalletsSection
modalType={modalType}
isLoading={isLoading}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { Box, Button, Flex, Stack, Text as CText } from '@chakra-ui/react'
import { useCallback, useEffect, useState } from 'react'
import { FaWallet } from 'react-icons/fa'
import { FoxIcon } from 'components/Icons/FoxIcon'
import { Text } from 'components/Text'
import { WalletActions } from 'context/WalletProvider/actions'
import { KeyManager } from 'context/WalletProvider/KeyManager'
import { useLocalWallet } from 'context/WalletProvider/local-wallet'
import { NativeConfig } from 'context/WalletProvider/NativeWallet/config'
import { useWallet } from 'hooks/useWallet/useWallet'

type VaultInfo = {
id: string
name: string
}

type WalletCardProps = {
wallet: VaultInfo
onClick: (wallet: VaultInfo) => void
}

const WalletCard = ({ wallet, onClick }: WalletCardProps) => {
const handleClick = useCallback(() => onClick(wallet), [onClick, wallet])
return (
<Box
as={Button}
key={wallet.id}
variant='ghost'
px={4}
ml={-4}
py={6}
borderRadius='md'
width='full'
onClick={handleClick}
>
<Flex alignItems='center' width='full'>
<FoxIcon boxSize='24px' mr={3}>
<FaWallet />
</FoxIcon>
<Box textAlign='left'>
<CText>{wallet.name}</CText>
</Box>
</Flex>
</Box>
)
}

export const SavedWalletsSection = () => {
const [wallets, setWallets] = useState<VaultInfo[]>([])
const localWallet = useLocalWallet()
const { getAdapter, dispatch } = useWallet()

// TODO(gomes): let's use this PR as an opportunity to remove this digusting IIAFE from copypasta, yes it works but pls
useEffect(() => {
;(async () => {
const Vault = await import('@shapeshiftoss/hdwallet-native-vault').then(m => m.Vault)
try {
const vaultIds = await Vault.list()
const storedWallets: VaultInfo[] = await Promise.all(
vaultIds.map(async id => {
const meta = await Vault.meta(id)
const name = String(meta?.get('name') ?? id)
return { id, name }
}),
)
setWallets(storedWallets)
} catch (e) {
console.error(e)
setWallets([])
}
})()
}, [])

const handleWalletSelect = useCallback(
async (wallet: VaultInfo) => {
const adapter = await getAdapter(KeyManager.Native)
const deviceId = wallet.id
if (adapter) {
const { name, icon } = NativeConfig
try {
dispatch({
type: WalletActions.SET_NATIVE_PENDING_DEVICE_ID,
payload: deviceId,
})

const walletInstance = await adapter.pairDevice(deviceId)
if (!(await walletInstance?.isInitialized())) {
await walletInstance?.initialize()
} else {
dispatch({
type: WalletActions.SET_WALLET,
payload: {
wallet: walletInstance,
name,
icon,
deviceId,
meta: { label: wallet.name },
connectedType: KeyManager.Native,
},
})
dispatch({
type: WalletActions.SET_IS_CONNECTED,
payload: true,
})
dispatch({ type: WalletActions.RESET_NATIVE_PENDING_DEVICE_ID })
dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: false })
}

localWallet.setLocalWallet({ type: KeyManager.Native, deviceId })
localWallet.setLocalNativeWalletName(wallet.name)
} catch (e) {
console.error(e)
}
}
},
[dispatch, getAdapter, localWallet],
)

if (!wallets.length) return null

return (
<Stack spacing={2} my={6}>
<Text
fontSize='sm'
fontWeight='medium'
color='gray.500'
translation={'walletProvider.shapeShift.load.header'}
/>
{wallets.map(wallet => (
<WalletCard wallet={wallet} onClick={handleWalletSelect} />
))}
</Stack>
)
}
Original file line number Diff line number Diff line change
@@ -69,7 +69,7 @@ export const NativeStart = () => {
</Box>

<Button colorScheme='blue' px={4} onClick={handleCreateClick}>
{translate('walletProvider.shapeShift.onboarding.createANewWallet')}
{translate('walletProvider.shapeShift.onboarding.createNewWallet')}
</Button>

<Button variant='link' color={bodyColor} mt={4} onClick={handleImportClick}>
5 changes: 5 additions & 0 deletions src/context/WalletProvider/WalletViewsSwitch.tsx
Original file line number Diff line number Diff line change
@@ -92,6 +92,11 @@ export const WalletViewsSwitch = () => {
await cancelWalletRequests()
}, [cancelWalletRequests, dispatch, history])

useEffect(() => {
// Reset on mount
dispatch({ type: WalletActions.SET_INITIAL_ROUTE, payload: '' })
}, [dispatch])

useEffect(() => {
if (initialRoute) {
history.push(initialRoute)