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

[Seedless Onboarding] Account center redesign #2607

Merged
merged 7 commits into from
Oct 11, 2023
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
2 changes: 1 addition & 1 deletion cypress/e2e/pages/create_wallet.pages.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as constants from '../../support/constants'

const newAccountBtnStr = 'Create new Account'
const newAccountBtnStr = 'Continue with E2E Wallet'

const nameInput = 'input[name="name"]'
const selectNetworkBtn = '[data-cy="create-safe-select-network"]'
Expand Down
6 changes: 5 additions & 1 deletion cypress/e2e/pages/import_export.pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function clickOnImportBtn() {
}

export function clickOnImportBtnDataImportModal() {
cy.contains(dataImportModalStr).parent().contains('button', 'Import').click()
cy.contains('button', 'Import').click()
}

export function uploadFile(filePath) {
Expand All @@ -43,6 +43,10 @@ export function clickOnImportedSafe(safe) {
cy.contains(safe).click()
}

export function clickOnOpenSafeListSidebar() {
cy.contains('My Safe Accounts').click()
}

export function clickOnClosePushNotificationsBanner() {
cy.waitForSelector(() => {
return cy.get('h6').contains(enablePushNotificationsStr).siblings('.MuiButtonBase-root').click({ force: true })
Expand Down
4 changes: 2 additions & 2 deletions cypress/e2e/pages/load_safe.pages.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as constants from '../../support/constants'

const addExistingAccountBtnStr = 'Add existing Account'
const addExistingAccountBtnStr = 'Add existing one'
const contactStr = 'Name, address & network'
const invalidAddressFormatErrorMsg = 'Invalid address format'

Expand All @@ -16,7 +16,7 @@ const ownersConfirmationsStr = 'Owners and confirmations'
const transactionStr = 'Transactions'

export function openLoadSafeForm() {
cy.contains('button', addExistingAccountBtnStr).click()
cy.contains('a', addExistingAccountBtnStr).click()
cy.contains(contactStr)
}

Expand Down
6 changes: 3 additions & 3 deletions cypress/e2e/smoke/import_export_data.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ import * as constants from '../../support/constants'
describe('Import Export Data', () => {
before(() => {
cy.clearLocalStorage()
cy.visit(constants.welcomeUrl)
cy.visit(constants.dataSettingsUrl)
main.acceptCookies()
file.verifyImportBtnIsVisible()
})

it('Uploads test file and access safe', () => {
const filePath = '../fixtures/data_import.json'
const safe = 'safe 1 goerli'

file.clickOnImportBtn()
file.uploadFile(filePath)
file.verifyImportModalData()
file.clickOnImportBtnDataImportModal()
cy.visit(constants.welcomeUrl)
file.clickOnOpenSafeListSidebar()
file.clickOnImportedSafe(safe)
file.clickOnClosePushNotificationsBanner()
})
Expand Down
1 change: 1 addition & 0 deletions cypress/support/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const requestPermissionsUrl = '/request-permissions'
export const getPermissionsUrl = '/get-permissions'
export const appSettingsUrl = '/settings/safe-apps'
export const setupUrl = '/settings/setup?safe='
export const dataSettingsUrl = '/settings/data'
export const invalidAppUrl = 'https://my-invalid-custom-app.com/manifest.json'
export const validAppUrlJson = 'https://my-valid-custom-app.com/manifest.json'
export const validAppUrl = 'https://my-valid-custom-app.com'
Expand Down
3 changes: 3 additions & 0 deletions public/images/common/lock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 27 additions & 29 deletions src/components/common/ConnectWallet/AccountCenter.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import type { MouseEvent } from 'react'
import { useState } from 'react'
import { Box, Button, ButtonBase, Paper, Popover, Typography } from '@mui/material'
import { Box, Button, ButtonBase, Paper, Popover } from '@mui/material'
import css from '@/components/common/ConnectWallet/styles.module.css'
import EthHashInfo from '@/components/common/EthHashInfo'
import ExpandLessIcon from '@mui/icons-material/ExpandLess'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import useOnboard, { switchWallet } from '@/hooks/wallets/useOnboard'
import { useAppSelector } from '@/store'
import { selectChainById } from '@/store/chainsSlice'
import Identicon from '@/components/common/Identicon'
import ChainSwitcher from '../ChainSwitcher'
import useAddressBook from '@/hooks/useAddressBook'
import { type ConnectedWallet } from '@/hooks/wallets/useOnboard'
import WalletInfo, { UNKNOWN_CHAIN_NAME } from '../WalletInfo'
import WalletInfo from '../WalletInfo'
import ChainIndicator from '@/components/common/ChainIndicator'
import { ONBOARD_MPC_MODULE_LABEL } from '@/services/mpc/module'
import SocialLoginInfo from '@/components/common/SocialLoginInfo'

const AccountCenter = ({ wallet }: { wallet: ConnectedWallet }) => {
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null)
Expand Down Expand Up @@ -53,7 +55,13 @@ const AccountCenter = ({ wallet }: { wallet: ConnectedWallet }) => {
<>
<ButtonBase onClick={handleClick} aria-describedby={id} disableRipple sx={{ alignSelf: 'stretch' }}>
<Box className={css.buttonContainer}>
<WalletInfo wallet={wallet} />
{wallet.label === ONBOARD_MPC_MODULE_LABEL ? (
<div className={css.socialLoginInfo}>
<SocialLoginInfo wallet={wallet} chainInfo={chainInfo} hideActions={true} />
</div>
) : (
<WalletInfo wallet={wallet} />
)}

<Box display="flex" alignItems="center" justifyContent="flex-end" marginLeft="auto">
{open ? <ExpandLessIcon color="border" /> : <ExpandMoreIcon color="border" />}
Expand All @@ -77,31 +85,21 @@ const AccountCenter = ({ wallet }: { wallet: ConnectedWallet }) => {
sx={{ marginTop: 1 }}
>
<Paper className={css.popoverContainer}>
<Identicon address={wallet.address} />

<Typography variant="h5" className={css.addressName}>
{addressBook[wallet.address] || wallet.ens}
</Typography>

<Box bgcolor="border.background" px={2} py={1} fontSize={14}>
<EthHashInfo
address={wallet.address}
showAvatar={false}
showName={false}
hasExplorer
showCopyButton
prefix={prefix}
/>
</Box>

<Box className={css.rowContainer}>
<Box className={css.row}>
<Typography variant="caption">Wallet</Typography>
<Typography variant="body2">{wallet.label}</Typography>
</Box>
<Box className={css.row}>
<Typography variant="caption">Connected network</Typography>
<Typography variant="body2">{chainInfo?.chainName || UNKNOWN_CHAIN_NAME}</Typography>
<Box className={css.accountContainer}>
<ChainIndicator />
<Box className={css.addressContainer}>
{wallet.label === ONBOARD_MPC_MODULE_LABEL ? (
<SocialLoginInfo wallet={wallet} chainInfo={chainInfo} />
) : (
<EthHashInfo
address={wallet.address}
name={addressBook[wallet.address] || wallet.ens}
hasExplorer
showCopyButton
prefix={prefix}
avatarSize={32}
/>
)}
</Box>
</Box>

Expand Down
50 changes: 26 additions & 24 deletions src/components/common/ConnectWallet/MPCLogin.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,48 @@
import { MPCWalletState } from '@/hooks/wallets/mpc/useMPCWallet'
import { Box, Button, SvgIcon, Typography } from '@mui/material'
import { useContext, useEffect, useState } from 'react'
import { useContext } from 'react'
import { MpcWalletContext } from './MPCWalletProvider'
import { PasswordRecovery } from './PasswordRecovery'
import GoogleLogo from '@/public/images/welcome/logo-google.svg'

import css from './styles.module.css'
import useWallet from '@/hooks/wallets/useWallet'
import { ONBOARD_MPC_MODULE_LABEL } from '@/services/mpc/module'

const MPCLogin = ({ onLogin }: { onLogin?: () => void }) => {
const { loginPending, triggerLogin, userInfo, walletState, recoverFactorWithPassword } = useContext(MpcWalletContext)
const { triggerLogin, userInfo, walletState, recoverFactorWithPassword } = useContext(MpcWalletContext)

const wallet = useWallet()

const [loginTriggered, setLoginTriggered] = useState(false)
const loginPending = walletState === MPCWalletState.AUTHENTICATING

const login = async () => {
setLoginTriggered(true)
await triggerLogin()
const success = await triggerLogin()

if (success) {
onLogin?.()
}
}

// If login was triggered through the Button we immediately continue if logged in
useEffect(() => {
if (loginTriggered && wallet && wallet.label === ONBOARD_MPC_MODULE_LABEL && onLogin) {
onLogin()
const recoverPassword = async (password: string, storeDeviceFactor: boolean) => {
const success = await recoverFactorWithPassword(password, storeDeviceFactor)

if (success) {
onLogin?.()
}
}, [loginTriggered, onLogin, wallet])
}

return (
<>
{wallet && userInfo ? (
<>
<Button
variant="outlined"
sx={{ padding: '1 2' }}
sx={{ px: 2, py: 1, borderWidth: '1px !important' }}
onClick={onLogin}
size="small"
disabled={loginPending}
fullWidth
>
<Box
width="100%"
justifyContent="space-between"
display="flex"
flexDirection="row"
alignItems="center"
gap={1}
>
<Box width="100%" display="flex" flexDirection="row" alignItems="center" gap={1}>
<img
src={userInfo.profileImage}
className={css.profileImg}
Expand All @@ -60,20 +55,27 @@ const MPCLogin = ({ onLogin }: { onLogin?: () => void }) => {
</Typography>
<Typography variant="body2">{userInfo.email}</Typography>
</div>
<SvgIcon component={GoogleLogo} inheritViewBox fontSize="medium" />
<SvgIcon component={GoogleLogo} inheritViewBox fontSize="medium" sx={{ marginLeft: 'auto' }} />
</Box>
</Button>
</>
) : (
<Button variant="outlined" onClick={login} size="small" disabled={loginPending} fullWidth>
<Button
variant="outlined"
onClick={login}
size="small"
disabled={loginPending}
fullWidth
sx={{ borderWidth: '1px !important' }}
>
<Box display="flex" flexDirection="row" alignItems="center" gap={1}>
<SvgIcon component={GoogleLogo} inheritViewBox fontSize="medium" /> Continue with Google
</Box>
</Button>
)}

{walletState === MPCWalletState.MANUAL_RECOVERY && (
<PasswordRecovery recoverFactorWithPassword={recoverFactorWithPassword} />
<PasswordRecovery recoverFactorWithPassword={recoverPassword} />
)}
</>
)
Expand Down
28 changes: 5 additions & 23 deletions src/components/common/ConnectWallet/MPCWalletProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,17 @@
import { useMPCWallet, MPCWalletState } from '@/hooks/wallets/mpc/useMPCWallet'
import { type UserInfo } from '@web3auth/mpc-core-kit'
import { useMPCWallet, MPCWalletState, type MPCWalletHook } from '@/hooks/wallets/mpc/useMPCWallet'
import { createContext, type ReactElement } from 'react'

type MPCWalletContext = {
loginPending: boolean
triggerLogin: () => Promise<void>
resetAccount: () => Promise<void>
upsertPasswordBackup: (password: string) => Promise<void>
recoverFactorWithPassword: (password: string, storeDeviceFactor: boolean) => Promise<void>
walletState: MPCWalletState
userInfo: UserInfo | undefined
}

export const MpcWalletContext = createContext<MPCWalletContext>({
loginPending: false,
export const MpcWalletContext = createContext<MPCWalletHook>({
walletState: MPCWalletState.NOT_INITIALIZED,
triggerLogin: () => Promise.resolve(),
triggerLogin: () => Promise.resolve(false),
resetAccount: () => Promise.resolve(),
upsertPasswordBackup: () => Promise.resolve(),
recoverFactorWithPassword: () => Promise.resolve(),
recoverFactorWithPassword: () => Promise.resolve(false),
userInfo: undefined,
})

export const MpcWalletProvider = ({ children }: { children: ReactElement }) => {
const mpcValue = useMPCWallet()

return (
<MpcWalletContext.Provider
value={{ ...mpcValue, loginPending: mpcValue.walletState === MPCWalletState.AUTHENTICATING }}
>
{children}
</MpcWalletContext.Provider>
)
return <MpcWalletContext.Provider value={mpcValue}>{children}</MpcWalletContext.Provider>
}
27 changes: 11 additions & 16 deletions src/components/common/ConnectWallet/WalletDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
import { Button, Typography } from '@mui/material'
import { Divider, Typography } from '@mui/material'
import type { ReactElement } from 'react'

import KeyholeIcon from '@/components/common/icons/KeyholeIcon'
import useConnectWallet from '@/components/common/ConnectWallet/useConnectWallet'
import LockIcon from '@/public/images/common/lock.svg'
import MPCLogin from './MPCLogin'
import WalletLogin from '@/components/welcome/WelcomeLogin/WalletLogin'

const WalletDetails = ({ onConnect }: { onConnect?: () => void }): ReactElement => {
const connectWallet = useConnectWallet()

const handleConnect = () => {
onConnect?.()
connectWallet()
}

const WalletDetails = ({ onConnect }: { onConnect: () => void }): ReactElement => {
return (
<>
<Typography variant="h5">Connect a wallet</Typography>
<LockIcon />

<KeyholeIcon />
<WalletLogin onLogin={onConnect} />

<Button onClick={handleConnect} variant="contained" size="small" disableElevation fullWidth>
Connect
</Button>
<Divider sx={{ width: '100%' }}>
<Typography color="text.secondary" fontWeight={700} variant="overline">
or
</Typography>
</Divider>

<MPCLogin />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('MPCLogin', () => {
const mockOnLogin = jest.fn()
const walletAddress = hexZeroPad('0x1', 20)
const mockUseWallet = jest.spyOn(useWallet, 'default').mockReturnValue(null)
const mockTriggerLogin = jest.fn()
const mockTriggerLogin = jest.fn(() => true)
const mockUseMPCWallet = jest.spyOn(useMPCWallet, 'useMPCWallet').mockReturnValue({
userInfo: {
email: undefined,
Expand Down
Loading
Loading