Skip to content

Commit

Permalink
Merge pull request #1630 from oasisprotocol/lw/to-support-settings
Browse files Browse the repository at this point in the history
Redesign towards settings page
  • Loading branch information
lukaw3d committed Sep 5, 2023
2 parents e1713fa + ebf5792 commit 87ab442
Show file tree
Hide file tree
Showing 18 changed files with 463 additions and 413 deletions.
2 changes: 1 addition & 1 deletion playwright/tests/persist.spec.ts
Expand Up @@ -115,7 +115,7 @@ test.describe('Persist', () => {

await page.getByTestId('account-selector').click({ timeout: 15_000 })
await expect(page.getByTestId('account-choice')).toHaveCount(1)
await page.getByRole('button', { name: /Close/ }).click()
await page.getByRole('button', { name: /Select/ }).click()
})
await test.step('Add another account after reloading and unlocking and it should persist', async () => {
await page.getByRole('link', { name: /Home/ }).click()
Expand Down
10 changes: 9 additions & 1 deletion src/app/components/AddressBox/index.tsx
Expand Up @@ -16,6 +16,7 @@ import { PrettyAddress } from '../PrettyAddress'

interface Props {
address: string
border?: boolean
}

export const AddressBox = memo((props: Props) => {
Expand All @@ -34,7 +35,14 @@ export const AddressBox = memo((props: Props) => {
}

return (
<Box direction="row" align="center" round="5px" pad={{ right: 'small' }} width="fit-content">
<Box
direction="row"
align="center"
round="5px"
pad={{ right: 'small' }}
width="fit-content"
border={props.border && { color: 'brand' }}
>
<Button onClick={() => copyAddress()} icon={<Copy size="18px" />} data-testid="copy-address" />
<Text weight="bold" size="medium" wordBreak="break-word">
<PrettyAddress address={address} />
Expand Down
93 changes: 93 additions & 0 deletions src/app/components/Toolbar/Features/Account/Account.tsx
@@ -0,0 +1,93 @@
import { AmountFormatter } from 'app/components/AmountFormatter'
import { PrettyAddress } from 'app/components/PrettyAddress'
import { ShortAddress } from 'app/components/ShortAddress'
import { Box } from 'grommet/es6/components/Box'
import { CheckBox } from 'grommet/es6/components/CheckBox'
import { Spinner } from 'grommet/es6/components/Spinner'
import { Text } from 'grommet/es6/components/Text'
import { ResponsiveContext } from 'grommet/es6/contexts/ResponsiveContext'
import { memo, useContext } from 'react'
import { useTranslation } from 'react-i18next'
import { BalanceDetails } from '../../../../state/account/types'
import { Button } from 'grommet/es6/components/Button'
import { DerivationFormatter, DerivationFormatterProps } from './DerivationFormatter'

interface AccountProps {
address: string
balance: BalanceDetails | undefined
onClick: (address: string) => void
path?: number[]
isActive: boolean
displayCheckbox?: boolean
displayAccountNumber?: boolean
displayDerivation?: DerivationFormatterProps
displayManageButton?: {
onClickManage: (address: string) => void
}
}

export const Account = memo((props: AccountProps) => {
const { t } = useTranslation()
const size = useContext(ResponsiveContext)

const address =
size === 'small' ? <ShortAddress address={props.address} /> : <PrettyAddress address={props.address} />

const accountNumber = props.path ? props.path.at(-1) : '?'

return (
<Box
data-testid="account-choice"
round="5px"
background={props.isActive ? 'neutral-2' : undefined}
border={{ color: props.isActive ? 'neutral-2' : 'brand' }}
pad="small"
flex="grow"
fill="horizontal"
role="checkbox"
aria-checked={props.isActive}
onClick={() => {
props.onClick(props.address)
}}
hoverIndicator={{ background: 'brand' }}
direction="row"
>
{props.displayCheckbox && (
<Box alignSelf="center" pad={{ left: 'small', right: 'medium' }}>
<CheckBox checked={props.isActive} hidden />
</Box>
)}
{props.displayAccountNumber && (
<Box alignSelf="center" pad={{ left: 'small', right: 'small' }} style={{ minWidth: '2.5em' }}>
<Text weight="bold" style={{ width: '2em' }}>
{accountNumber}
</Text>
</Box>
)}
<Box flex="grow" gap={size === 'small' ? undefined : 'xsmall'}>
<Box>
<Text weight="bold">{address}</Text>
</Box>
<Box direction="row-responsive">
<Box flex="grow">
{props.displayDerivation && <DerivationFormatter {...props.displayDerivation} />}
{props.displayManageButton && (
<Box direction="row">
<Button
label={t('toolbar.settings.manageAccount', 'Manage')}
onClick={e => {
props.displayManageButton?.onClickManage(props.address)
e.stopPropagation()
}}
/>
</Box>
)}
</Box>
<Box height={'24px'}>
{props.balance ? <AmountFormatter amount={props.balance.total} /> : <Spinner />}
</Box>
</Box>
</Box>
</Box>
)
})
@@ -0,0 +1,23 @@
import { WalletType } from 'app/state/wallet/types'
import { Box } from 'grommet/es6/components/Box'
import { Text } from 'grommet/es6/components/Text'
import { useTranslation } from 'react-i18next'

export interface DerivationFormatterProps {
type: WalletType
pathDisplay: string | undefined
}

export const DerivationFormatter = (props: DerivationFormatterProps) => {
const { t } = useTranslation()
const walletTypes: { [type in WalletType]: string } = {
[WalletType.Ledger]: t('toolbar.wallets.type.ledger', 'Ledger'),
[WalletType.Mnemonic]: t('toolbar.wallets.type.mnemonic', 'Mnemonic'),
[WalletType.PrivateKey]: t('toolbar.wallets.type.privateKey', 'Private key'),
}
return (
<Box align="start" direction="row">
{walletTypes[props.type]} {props.pathDisplay && <Text size="80%">({props.pathDisplay})</Text>}
</Box>
)
}
26 changes: 26 additions & 0 deletions src/app/components/Toolbar/Features/Account/ImportableAccount.tsx
@@ -0,0 +1,26 @@
import { Account } from './Account'
import { ImportAccountsListAccount } from '../../../../state/importaccounts/types'

export const ImportableAccount = ({
account,
onClick,
}: {
account: ImportAccountsListAccount
onClick: (address: string) => void
}) => {
return (
<Account
address={account.address}
balance={account.balance}
onClick={onClick}
isActive={account.selected}
displayCheckbox={true}
displayAccountNumber={true}
displayDerivation={{
type: account.type,
pathDisplay: account.pathDisplay,
}}
path={account.path}
/>
)
}
83 changes: 83 additions & 0 deletions src/app/components/Toolbar/Features/Account/ManageableAccount.tsx
@@ -0,0 +1,83 @@
import { Box } from 'grommet/es6/components/Box'
import { Button } from 'grommet/es6/components/Button'
import { Tab } from 'grommet/es6/components/Tab'
import { Text } from 'grommet/es6/components/Text'
import { ResponsiveContext } from 'grommet/es6/contexts/ResponsiveContext'
import { useContext, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Account } from './Account'
import { Wallet } from '../../../../state/wallet/types'
import { ResponsiveLayer } from '../../../ResponsiveLayer'
import { Tabs } from 'grommet/es6/components/Tabs'
import { DerivationFormatter } from './DerivationFormatter'
import { uintToBase64, hex2uint } from '../../../../lib/helpers'
import { AddressBox } from '../../../AddressBox'

export const ManageableAccount = ({
wallet,
isActive,
onClick,
}: {
wallet: Wallet
isActive: boolean
onClick: (address: string) => void
}) => {
const { t } = useTranslation()
const [layerVisibility, setLayerVisibility] = useState(false)
const isMobile = useContext(ResponsiveContext) === 'small'
return (
<>
<Account
address={wallet.address}
balance={wallet.balance}
onClick={onClick}
isActive={isActive}
path={wallet.path}
displayManageButton={{
onClickManage: () => setLayerVisibility(true),
}}
/>
{layerVisibility && (
<ResponsiveLayer
onClickOutside={() => setLayerVisibility(false)}
onEsc={() => setLayerVisibility(false)}
animation="none"
background="background-front"
modal
position="top"
margin={isMobile ? 'none' : 'xlarge'}
>
<Box margin="medium" width={isMobile ? 'auto' : '700px'}>
<Tabs alignControls="start">
<Tab title={t('toolbar.settings.myAccountsTab', 'My Accounts')}>
<Box margin={{ vertical: 'medium' }}>
<AddressBox address={wallet.address} border />
<Text size="small" margin={'small'}>
<DerivationFormatter pathDisplay={wallet.pathDisplay} type={wallet.type} />
</Text>
</Box>
<Button
label={t('toolbar.settings.exportPrivateKey', 'Export Private Key')}
disabled={!wallet.privateKey}
onClick={() => {
prompt(
t('toolbar.settings.exportPrivateKey', 'Export Private Key'),
uintToBase64(hex2uint(wallet.privateKey!)),
)
}}
/>
<Box direction="row" justify="between" pad={{ top: 'large' }}>
<Button
secondary
label={t('toolbar.settings.cancel', 'Cancel')}
onClick={() => setLayerVisibility(false)}
/>
</Box>
</Tab>
</Tabs>
</Box>
</ResponsiveLayer>
)}
</>
)
}
Expand Up @@ -5,7 +5,7 @@ import { Provider } from 'react-redux'
import { configureAppStore } from 'store/configureStore'
import { ThemeProvider } from 'styles/theme/ThemeProvider'

import { Account } from '..'
import { Account } from '../Account'

const renderComponent = (store: any) =>
render(
Expand All @@ -14,13 +14,15 @@ const renderComponent = (store: any) =>
<Account
address="oasis1qq3xrq0urs8qcffhvmhfhz4p0mu7ewc8rscnlwxe"
balance={{ available: '200', debonding: '0', delegations: '800', total: '1000' }}
type={WalletType.Mnemonic}
onClick={() => {}}
isActive={false}
displayCheckbox={true}
displayAccountNumber={true}
path={[44, 474, 0, 0, 0]}
pathDisplay="m/44'/474'/0'/0'/0'"
displayDerivation={{
type: WalletType.Mnemonic,
pathDisplay: "m/44'/474'/0'/0'/0'",
}}
/>
</ThemeProvider>
</Provider>,
Expand Down
Expand Up @@ -26,8 +26,8 @@ exports[`<Account /> should match snapshot 1`] = `
}
.c14 {
font-size: 14px;
line-height: 20px;
font-size: 80%;
line-height: normal;
}
.c1 {
Expand Down Expand Up @@ -204,9 +204,6 @@ exports[`<Account /> should match snapshot 1`] = `
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex: 1 0 auto;
-ms-flex: 1 0 auto;
flex: 1 0 auto;
}
.c15 {
Expand Down Expand Up @@ -396,17 +393,21 @@ exports[`<Account /> should match snapshot 1`] = `
class="c12"
>
<div
class="c13"
class="c10"
>
toolbar.wallets.type.mnemonic
<span
class="c14"
<div
class="c13"
>
(
m/44'/474'/0'/0'/0'
)
</span>
toolbar.wallets.type.mnemonic
<span
class="c14"
>
(
m/44'/474'/0'/0'/0'
)
</span>
</div>
</div>
<div
class="c15"
Expand Down

0 comments on commit 87ab442

Please sign in to comment.