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
Show file tree
Hide file tree
Changes from 11 commits
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
7 changes: 7 additions & 0 deletions src/assets/translations/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -1871,6 +1871,13 @@
"onboarding": {
"skip": "Skip",
"shapeshiftWallet": "ShapeShift Wallet",
"createNewWallet": "Create a new wallet",
"haveAKeystore": "Have a keystore?",
"importExisting": "Import existing",
"importFromKeystore": "Import from keystore",
"whatIsShapeshiftWallet": "What is ShapeShift Wallet?",
"yourDecentralizedGateway": "Your decentralization gateway. Trade and earn Bitcoin, Ethereum, Solana and more from a single, secure wallet.",
"crossChainFreedom": "Cross chain freedom. Access the best rates across multiple decentralized exchanges, no middleman required",
"selfCustody": {
"title": "Your ShapeShift Wallet is Self-Custody",
"subTitle": "You hold the keys and you're the only one who can access it.",
Expand Down
2 changes: 1 addition & 1 deletion src/context/WalletProvider/NativeWallet/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { NativeAdapter } from '@shapeshiftoss/hdwallet-native'
import { FoxIcon } from 'components/Icons/FoxIcon' // Ensure the import path is correct
import { FoxIcon } from 'components/Icons/FoxIcon'
import type { SupportedWalletInfo } from 'context/WalletProvider/config'

type NativeConfigType = Omit<SupportedWalletInfo<typeof NativeAdapter>, 'routes'>
Expand Down
12 changes: 1 addition & 11 deletions src/context/WalletProvider/NativeWallet/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
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
Expand All @@ -22,12 +20,4 @@ export interface LocationState {
}
}

export interface NativeSetupProps
extends RouteComponentProps<
{},
any, // history
LocationState
> {
vault: Vault
dispatch: React.Dispatch<ActionTypes>
}
export type NativeSetupProps = RouteComponentProps<{}, any, LocationState>
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved
191 changes: 131 additions & 60 deletions src/context/WalletProvider/NewWalletViews/NewWalletViewsSwitch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,33 @@ import {
useColorModeValue,
useToast,
} from '@chakra-ui/react'
import type { Location } from 'history'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { isMobile } from 'react-device-detect'
import { useTranslate } from 'react-polyglot'
import type { StaticContext } from 'react-router'
import type { RouteComponentProps } from 'react-router-dom'
import { Route, Switch, useHistory } 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 { NativeImportSeed } from '../NativeWallet/components/NativeImportSeed'
import { NativeImportSelect } from '../NativeWallet/components/NativeImportSelect'
import { NativePassword } from '../NativeWallet/components/NativePassword'
import { NativeSuccess } from '../NativeWallet/components/NativeSuccess'
import { NativeTestPhrase } from '../NativeWallet/components/NativeTestPhrase'
import type { NativeSetupProps } 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'

const sectionsWidth = { base: 'full', md: '300px' }
const containerWidth = {
Expand All @@ -38,18 +52,87 @@ type RightPanelContentProps = {
setIsLoading: (loading: boolean) => void
error: string | null
setError: (error: string | null) => void
location: Location
}

const nativeRoutes = (
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved
<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.ImportSeed}
// TODO(gomes): add NativeImportSelectNew 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 => <NativeImportSeed {...routeProps} />}
/>
<Route
exact
path={NativeWalletRoutes.ImportSelect}
// TODO(gomes): add NativeImportSelectNew 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 => <NativeImportSelect {...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 NativeSetupProps)} />}
/>
<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 NativeSetupProps)} />}
/>
<Route
exact
path={NativeWalletRoutes.CreateTest}
// TODO(gomes): add NativeTestPhraseNew 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 => <NativeTestPhrase {...(routeProps as NativeSetupProps)} />}
/>
</Switch>
)

const RightPanelContent = ({
isLoading,
setIsLoading,
error,
setError,
location,
}: RightPanelContentProps) => {
const {
state: { modalType, isMipdProvider },
} = useWallet()

if (location.pathname.startsWith('/native')) return nativeRoutes

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

if (isMipdProvider && modalType) {
return (
<Switch>
Expand All @@ -63,27 +146,10 @@ const RightPanelContent = ({
/>
</Route>
<Route path='/metamask/snap/install'>
<Flex height='full' alignItems='center'>
<Box width='full'>
<SnapInstall />
</Box>
</Flex>
<SnapInstall />
</Route>
<Route path='/metamask/snap/update'>
<Flex height='full' alignItems='center'>
<Box width='full'>
<SnapUpdate />
</Box>
</Flex>
</Route>
<Route path='/'>
<MipdBody
rdns={modalType}
isLoading={isLoading}
error={error}
setIsLoading={setIsLoading}
setError={setError}
/>
<SnapUpdate />
</Route>
</Switch>
)
Expand Down Expand Up @@ -159,54 +225,42 @@ export const NewWalletViewsSwitch = () => {
wallet,
])

// Push to history on initialRoute change
useEffect(() => {
if (initialRoute) history.push(initialRoute)
return
}, [history, initialRoute])
// Reset history on modal open/unmount
useEffect(() => {
history.replace('/')

// Reset initial route on connect to handle e.g switching from MM with snap install route to another mipd provider
const handleConnect = useCallback(() => {
if (initialRoute) history.push(initialRoute)
}, [history, initialRoute])
return () => {
history.replace('/')
}
// Only run this on initial render, and unmount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

const sections = useMemo(
() => (
<Box w={sectionsWidth} p={6}>
<Text translation='common.connectWallet' fontSize='xl' fontWeight='semibold' />
<InstalledWalletsSection
modalType={modalType}
isLoading={isLoading}
onConnect={handleConnect}
/>
<SavedWalletsSection />
<InstalledWalletsSection modalType={modalType} isLoading={isLoading} />
{/* TODO(gomes): more sections */}
</Box>
),
[handleConnect, isLoading, modalType],
[isLoading, modalType],
)

const bodyBgColor = useColorModeValue('gray.50', 'whiteAlpha.50')
const buttonContainerBgColor = useColorModeValue('gray.100', 'whiteAlpha.100')
const body = useMemo(() => {
return (
<Box flex={1} bg={bodyBgColor} p={6}>
<RightPanelContent
isLoading={isLoading}
setIsLoading={setIsLoading}
error={error}
setError={setError}
/>
</Box>
)
}, [bodyBgColor, error, isLoading])

const maybeMobileBackButton = useMemo(() => {
if (!isMobile) return
return (
<Switch>
<Route exact path='/' />
<Route path='*'>
{/* Precisely what it says on the var name - adds a back button for mobile only, and for non-root paths only
* (i.e, can't go back when in root path)
*/}
const body = useCallback(
(routeProps: RouteComponentProps<{}, StaticContext, unknown>) => (
<Box flex={1} bg={bodyBgColor} p={6} position='relative'>
{routeProps.history.location.pathname !== '/' ? (
// Only show back button for non-root routes
<Box
position='absolute'
left={3}
Expand All @@ -226,10 +280,31 @@ export const NewWalletViewsSwitch = () => {
onClick={handleBack}
/>
</Box>
</Route>
</Switch>
)
}, [buttonContainerBgColor, handleBack, translate])
) : null}
<Flex height='full' alignItems='center'>
<Box width='full'>
<RightPanelContent
location={routeProps.history.location}
isLoading={isLoading}
setIsLoading={setIsLoading}
error={error}
setError={setError}
/>
</Box>
</Flex>
</Box>
),
[bodyBgColor, buttonContainerBgColor, error, handleBack, isLoading, translate],
)

const bodyDesktopOnly = useCallback(
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved
(routeProps: RouteComponentProps<{}, StaticContext, unknown>) => {
if (isMobile) return null

return body(routeProps)
},
[body],
)

return (
<>
Expand All @@ -253,8 +328,6 @@ export const NewWalletViewsSwitch = () => {
>
<ModalCloseButton position='static' borderRadius='full' size='sm' />
</Box>

{maybeMobileBackButton}
<Flex minH='600px' w={containerWidth}>
<Switch>
{/* Always display sections for the root route, no matter the viewport */}
Expand All @@ -266,11 +339,9 @@ export const NewWalletViewsSwitch = () => {
</Switch>
<Switch>
{/* Only display side panel after a wallet has been selected on mobile */}
<Route exact path='/'>
{!isMobile ? body : null}
</Route>
<Route exact path='/' render={bodyDesktopOnly} />
{/* And for all non-root routes, no matter the viewport */}
<Route path='*'>{body}</Route>
<Route path='*' render={body} />
</Switch>
</Flex>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { EIP6963ProviderDetail } from 'mipd'
import { useCallback, useMemo } from 'react'
import { isMobile } from 'react-device-detect'
import { Text } from 'components/Text'
import { WalletActions } from 'context/WalletProvider/actions'
import type { KeyManager } from 'context/WalletProvider/KeyManager'
import { useWallet } from 'hooks/useWallet/useWallet'
import { isMobile as isMobileApp } from 'lib/globals'
Expand Down Expand Up @@ -50,13 +51,11 @@ const MipdProviderSelectItem = ({
export const InstalledWalletsSection = ({
modalType,
isLoading,
onConnect,
}: {
modalType: string | null
isLoading: boolean
onConnect: () => void
}) => {
const { connect } = useWallet()
const { dispatch, connect } = useWallet()
const detectedMipdProviders = useMipdProviders()

const supportedStaticProviders = useMemo(() => {
Expand Down Expand Up @@ -91,9 +90,9 @@ export const InstalledWalletsSection = ({
const handleConnectMipd = useCallback(
(rdns: string) => {
connect(rdns as KeyManager, true)
onConnect()
dispatch({ type: WalletActions.SET_INITIAL_ROUTE, payload: '/metamask/connect' })
},
[connect, onConnect],
[connect, dispatch],
)

return (
Expand Down
Loading
Loading