Merge pull request #119 from gnosis/edit-remove-safe
Safe settings: Change Safe name & Remove Safe
This commit is contained in:
commit
3201cce218
37
package.json
37
package.json
|
@ -32,13 +32,13 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gnosis.pm/safe-contracts": "^1.0.0",
|
"@gnosis.pm/safe-contracts": "^1.0.0",
|
||||||
"@gnosis.pm/util-contracts": "2.0.1",
|
"@gnosis.pm/util-contracts": "2.0.1",
|
||||||
"@material-ui/core": "4.1.1",
|
"@material-ui/core": "4.1.3",
|
||||||
"@material-ui/icons": "4.2.0",
|
"@material-ui/icons": "4.2.1",
|
||||||
"@welldone-software/why-did-you-render": "3.2.1",
|
"@welldone-software/why-did-you-render": "3.2.1",
|
||||||
"axios": "0.19.0",
|
"axios": "0.19.0",
|
||||||
"bignumber.js": "9.0.0",
|
"bignumber.js": "9.0.0",
|
||||||
"connected-react-router": "^6.3.1",
|
"connected-react-router": "^6.3.1",
|
||||||
"final-form": "4.15.0",
|
"final-form": "4.16.1",
|
||||||
"history": "^4.7.2",
|
"history": "^4.7.2",
|
||||||
"immortal-db": "^1.0.2",
|
"immortal-db": "^1.0.2",
|
||||||
"immutable": "^4.0.0-rc.9",
|
"immutable": "^4.0.0-rc.9",
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
"qrcode.react": "^0.9.3",
|
"qrcode.react": "^0.9.3",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
"react-final-form": "6.2.1",
|
"react-final-form": "6.3.0",
|
||||||
"react-final-form-listeners": "^1.0.2",
|
"react-final-form-listeners": "^1.0.2",
|
||||||
"react-hot-loader": "4.11.1",
|
"react-hot-loader": "4.11.1",
|
||||||
"react-infinite-scroll-component": "^4.5.2",
|
"react-infinite-scroll-component": "^4.5.2",
|
||||||
|
@ -86,10 +86,11 @@
|
||||||
"@babel/preset-flow": "^7.0.0-beta.40",
|
"@babel/preset-flow": "^7.0.0-beta.40",
|
||||||
"@babel/preset-react": "^7.0.0-beta.40",
|
"@babel/preset-react": "^7.0.0-beta.40",
|
||||||
"@sambego/storybook-state": "^1.0.7",
|
"@sambego/storybook-state": "^1.0.7",
|
||||||
"@storybook/addon-actions": "5.1.8",
|
"@storybook/addon-actions": "5.1.9",
|
||||||
"@storybook/addon-knobs": "5.1.8",
|
"@storybook/addon-knobs": "5.1.9",
|
||||||
"@storybook/addon-links": "5.1.8",
|
"@storybook/addon-links": "5.1.9",
|
||||||
"@storybook/react": "5.1.8",
|
"@storybook/react": "5.1.9",
|
||||||
|
"@testing-library/react": "^8.0.1",
|
||||||
"autoprefixer": "9.6.0",
|
"autoprefixer": "9.6.0",
|
||||||
"babel-core": "^7.0.0-bridge.0",
|
"babel-core": "^7.0.0-bridge.0",
|
||||||
"babel-eslint": "10.0.2",
|
"babel-eslint": "10.0.2",
|
||||||
|
@ -103,15 +104,15 @@
|
||||||
"detect-port": "^1.2.2",
|
"detect-port": "^1.2.2",
|
||||||
"eslint": "^5.16.0",
|
"eslint": "^5.16.0",
|
||||||
"eslint-config-airbnb": "^17.1.0",
|
"eslint-config-airbnb": "^17.1.0",
|
||||||
"eslint-plugin-flowtype": "3.10.3",
|
"eslint-plugin-flowtype": "3.11.1",
|
||||||
"eslint-plugin-import": "2.17.3",
|
"eslint-plugin-import": "2.18.0",
|
||||||
"eslint-plugin-jest": "22.6.4",
|
"eslint-plugin-jest": "22.7.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.0.3",
|
"eslint-plugin-jsx-a11y": "^6.0.3",
|
||||||
"eslint-plugin-react": "7.13.0",
|
"eslint-plugin-react": "7.14.2",
|
||||||
"ethereumjs-abi": "^0.6.7",
|
"ethereumjs-abi": "^0.6.7",
|
||||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||||
"file-loader": "4.0.0",
|
"file-loader": "4.0.0",
|
||||||
"flow-bin": "0.101.0",
|
"flow-bin": "0.102.0",
|
||||||
"fs-extra": "8.0.1",
|
"fs-extra": "8.0.1",
|
||||||
"html-loader": "^0.5.5",
|
"html-loader": "^0.5.5",
|
||||||
"html-webpack-plugin": "^3.0.4",
|
"html-webpack-plugin": "^3.0.4",
|
||||||
|
@ -128,13 +129,13 @@
|
||||||
"storybook-host": "^5.0.3",
|
"storybook-host": "^5.0.3",
|
||||||
"storybook-router": "^0.3.3",
|
"storybook-router": "^0.3.3",
|
||||||
"style-loader": "^0.23.1",
|
"style-loader": "^0.23.1",
|
||||||
"truffle": "5.0.22",
|
"truffle": "5.0.24",
|
||||||
"truffle-contract": "4.0.20",
|
"truffle-contract": "4.0.21",
|
||||||
"truffle-solidity-loader": "0.1.21",
|
"truffle-solidity-loader": "0.1.23",
|
||||||
"uglifyjs-webpack-plugin": "2.1.3",
|
"uglifyjs-webpack-plugin": "2.1.3",
|
||||||
"webpack": "4.34.0",
|
"webpack": "4.35.0",
|
||||||
"webpack-bundle-analyzer": "3.3.2",
|
"webpack-bundle-analyzer": "3.3.2",
|
||||||
"webpack-cli": "3.3.4",
|
"webpack-cli": "3.3.5",
|
||||||
"webpack-dev-server": "3.7.2",
|
"webpack-dev-server": "3.7.2",
|
||||||
"webpack-manifest-plugin": "^2.0.0-rc.2"
|
"webpack-manifest-plugin": "^2.0.0-rc.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,13 +43,12 @@ git clone https://github.com/gnosis/safe-contracts.git
|
||||||
cd safe-contracts
|
cd safe-contracts
|
||||||
yarn
|
yarn
|
||||||
ganache-cli -l 7000000
|
ganache-cli -l 7000000
|
||||||
npx truffle compile
|
|
||||||
npx truffle migrate
|
npx truffle migrate
|
||||||
```
|
```
|
||||||
2. Compiling Token Contracts for the tests:
|
2. Migrate Token Contracts for the tests:
|
||||||
Inside `safe-react` directory
|
Inside `safe-react` directory
|
||||||
```
|
```
|
||||||
npx truffle compile
|
npx truffle migrate
|
||||||
```
|
```
|
||||||
3. Run the tests:
|
3. Run the tests:
|
||||||
```
|
```
|
||||||
|
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
@ -6,7 +6,7 @@ import styles from './index.scss'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4';
|
type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
align?: 'left' | 'center' | 'right',
|
align?: 'left' | 'center' | 'right',
|
||||||
|
@ -15,24 +15,18 @@ type Props = {
|
||||||
tag: HeadingTag,
|
tag: HeadingTag,
|
||||||
truncate?: boolean,
|
truncate?: boolean,
|
||||||
children: React.Node,
|
children: React.Node,
|
||||||
|
testId?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
class Heading extends React.PureComponent<Props> {
|
class Heading extends React.PureComponent<Props> {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
align, tag, truncate, margin, color, children, ...props
|
align, tag, truncate, margin, color, children, testId, ...props
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
const className = cx(
|
const className = cx('heading', align, tag, margin ? capitalize(margin, 'margin') : undefined, color, { truncate })
|
||||||
'heading',
|
|
||||||
align,
|
|
||||||
tag,
|
|
||||||
margin ? capitalize(margin, 'margin') : undefined,
|
|
||||||
color,
|
|
||||||
{ truncate },
|
|
||||||
)
|
|
||||||
|
|
||||||
return React.createElement(tag, { ...props, className }, children)
|
return React.createElement(tag, { ...props, className, 'data-testid': testId || '' }, children)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import Block from '~/components/layout/Block'
|
|
||||||
import { withStyles } from '@material-ui/core/styles'
|
import { withStyles } from '@material-ui/core/styles'
|
||||||
|
import OpenInNew from '@material-ui/icons/OpenInNew'
|
||||||
|
import Block from '~/components/layout/Block'
|
||||||
import Field from '~/components/forms/Field'
|
import Field from '~/components/forms/Field'
|
||||||
import { required } from '~/components/forms/validator'
|
import { required } from '~/components/forms/validator'
|
||||||
import TextField from '~/components/forms/TextField'
|
import TextField from '~/components/forms/TextField'
|
||||||
import OpenInNew from '@material-ui/icons/OpenInNew'
|
|
||||||
import Identicon from '~/components/Identicon'
|
import Identicon from '~/components/Identicon'
|
||||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||||
import Col from '~/components/layout/Col'
|
import Col from '~/components/layout/Col'
|
||||||
|
@ -78,10 +78,6 @@ type Props = LayoutProps & {
|
||||||
updateInitialProps: (initialValues: Object) => void,
|
updateInitialProps: (initialValues: Object) => void,
|
||||||
}
|
}
|
||||||
|
|
||||||
type State = {
|
|
||||||
owners: Array<string>,
|
|
||||||
}
|
|
||||||
|
|
||||||
const calculateSafeValues = (owners: Array<string>, threshold: Number, values: Object) => {
|
const calculateSafeValues = (owners: Array<string>, threshold: Number, values: Object) => {
|
||||||
const initialValues = { ...values }
|
const initialValues = { ...values }
|
||||||
for (let i = 0; i < owners.length; i += 1) {
|
for (let i = 0; i < owners.length; i += 1) {
|
||||||
|
@ -91,41 +87,35 @@ const calculateSafeValues = (owners: Array<string>, threshold: Number, values: O
|
||||||
return initialValues
|
return initialValues
|
||||||
}
|
}
|
||||||
|
|
||||||
class OwnerListComponent extends React.PureComponent<Props, State> {
|
const OwnerListComponent = (props: Props) => {
|
||||||
state = {
|
const [owners, setOwners] = useState<Array<string>>([])
|
||||||
owners: [],
|
const {
|
||||||
}
|
values, updateInitialProps, network, classes,
|
||||||
|
} = props
|
||||||
|
|
||||||
mounted = false
|
useEffect(() => {
|
||||||
|
let isCurrent = true
|
||||||
|
|
||||||
componentDidMount = async () => {
|
const fetchSafe = async () => {
|
||||||
this.mounted = true
|
|
||||||
const { values, updateInitialProps } = this.props
|
|
||||||
const safeAddress = values[FIELD_LOAD_ADDRESS]
|
const safeAddress = values[FIELD_LOAD_ADDRESS]
|
||||||
|
|
||||||
const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
|
const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
|
||||||
const owners = await gnosisSafe.getOwners()
|
const safeOwners = await gnosisSafe.getOwners()
|
||||||
const threshold = await gnosisSafe.getThreshold()
|
const threshold = await gnosisSafe.getThreshold()
|
||||||
|
|
||||||
const initialValues = calculateSafeValues(owners.sort(), threshold, values)
|
if (isCurrent && owners) {
|
||||||
|
const sortedOwners = safeOwners.sort()
|
||||||
|
const initialValues = calculateSafeValues(sortedOwners, threshold, values)
|
||||||
updateInitialProps(initialValues)
|
updateInitialProps(initialValues)
|
||||||
|
setOwners(sortedOwners)
|
||||||
if (!owners) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.mounted) {
|
|
||||||
this.setState(() => ({ owners: owners.sort() }))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
fetchSafe()
|
||||||
this.mounted = false
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
return () => {
|
||||||
const { network, classes } = this.props
|
isCurrent = false
|
||||||
const { owners } = this.state
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
@ -141,8 +131,8 @@ class OwnerListComponent extends React.PureComponent<Props, State> {
|
||||||
</Row>
|
</Row>
|
||||||
<Hairline />
|
<Hairline />
|
||||||
<Block margin="md" padding="md">
|
<Block margin="md" padding="md">
|
||||||
{owners.map((x, index) => (
|
{owners.map((address, index) => (
|
||||||
<Row key={owners[index].address} className={classes.owner}>
|
<Row key={address} className={classes.owner}>
|
||||||
<Col xs={4}>
|
<Col xs={4}>
|
||||||
<Field
|
<Field
|
||||||
className={classes.name}
|
className={classes.name}
|
||||||
|
@ -150,18 +140,18 @@ class OwnerListComponent extends React.PureComponent<Props, State> {
|
||||||
component={TextField}
|
component={TextField}
|
||||||
type="text"
|
type="text"
|
||||||
validate={required}
|
validate={required}
|
||||||
defaultValue={`Owner #${index + 1}`}
|
initialValue={`Owner #${index + 1}`}
|
||||||
placeholder="Owner Name*"
|
placeholder="Owner Name*"
|
||||||
text="Owner Name"
|
text="Owner Name"
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={7}>
|
<Col xs={7}>
|
||||||
<Row className={classes.ownerAddresses}>
|
<Row className={classes.ownerAddresses}>
|
||||||
<Identicon address={owners[index]} diameter={32} />
|
<Identicon address={address} diameter={32} />
|
||||||
<Paragraph size="md" color="disabled" noMargin className={classes.address}>
|
<Paragraph size="md" color="disabled" noMargin className={classes.address}>
|
||||||
{owners[index]}
|
{address}
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
<Link className={classes.open} to={getEtherScanLink(owners[index], network)} target="_blank">
|
<Link className={classes.open} to={getEtherScanLink(address, network)} target="_blank">
|
||||||
<OpenInNew style={openIconStyle} />
|
<OpenInNew style={openIconStyle} />
|
||||||
</Link>
|
</Link>
|
||||||
</Row>
|
</Row>
|
||||||
|
@ -172,18 +162,13 @@ class OwnerListComponent extends React.PureComponent<Props, State> {
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const OwnerListPage = withStyles(styles)(OwnerListComponent)
|
const OwnerListPage = withStyles(styles)(OwnerListComponent)
|
||||||
|
|
||||||
const OwnerList = ({ updateInitialProps }: Object, network: string) => (controls: React$Node, { values }: Object) => (
|
const OwnerList = ({ updateInitialProps }: Object, network: string) => (controls: React$Node, { values }: Object) => (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<OpenPaper controls={controls} padding={false}>
|
<OpenPaper controls={controls} padding={false}>
|
||||||
<OwnerListPage
|
<OwnerListPage network={network} updateInitialProps={updateInitialProps} values={values} />
|
||||||
network={network}
|
|
||||||
updateInitialProps={updateInitialProps}
|
|
||||||
values={values}
|
|
||||||
/>
|
|
||||||
</OpenPaper>
|
</OpenPaper>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// @flow
|
// @flow
|
||||||
export const FIELD_LOAD_NAME: string = 'name'
|
export const FIELD_LOAD_NAME: string = 'name'
|
||||||
export const FIELD_LOAD_ADDRESS: string = 'address'
|
export const FIELD_LOAD_ADDRESS: string = 'address'
|
||||||
export const THRESHOLD: Number = 'threshold'
|
export const THRESHOLD: string = 'threshold'
|
||||||
|
|
|
@ -23,8 +23,7 @@ import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||||
import { getAccountsFrom } from '~/routes/open/utils/safeDataExtractor'
|
import { getAccountsFrom } from '~/routes/open/utils/safeDataExtractor'
|
||||||
import Hairline from '~/components/layout/Hairline'
|
import Hairline from '~/components/layout/Hairline'
|
||||||
import { md, lg, sm } from '~/theme/variables'
|
import { md, lg, sm } from '~/theme/variables'
|
||||||
|
import trash from '~/assets/icons/trash.svg'
|
||||||
const trash = require('../../assets/trash.svg')
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
classes: Object,
|
classes: Object,
|
||||||
|
|
|
@ -42,7 +42,7 @@ const Tokens = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Row align="center" grow className={classes.heading}>
|
<Row align="center" grow className={classes.heading}>
|
||||||
<Paragraph className={classes.manage} noMargin>
|
<Paragraph className={classes.manage} noMargin weight="bolder">
|
||||||
Manage Tokens
|
Manage Tokens
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
<IconButton onClick={onClose} disableRipple data-testid={MANAGE_TOKENS_MODAL_CLOSE_BUTTON_TEST_ID}>
|
<IconButton onClick={onClose} disableRipple data-testid={MANAGE_TOKENS_MODAL_CLOSE_BUTTON_TEST_ID}>
|
||||||
|
|
|
@ -3,10 +3,10 @@ import * as React from 'react'
|
||||||
import OpenInNew from '@material-ui/icons/OpenInNew'
|
import OpenInNew from '@material-ui/icons/OpenInNew'
|
||||||
import Tabs from '@material-ui/core/Tabs'
|
import Tabs from '@material-ui/core/Tabs'
|
||||||
import Tab from '@material-ui/core/Tab'
|
import Tab from '@material-ui/core/Tab'
|
||||||
|
import { withStyles } from '@material-ui/core/styles'
|
||||||
import Hairline from '~/components/layout/Hairline'
|
import Hairline from '~/components/layout/Hairline'
|
||||||
import Block from '~/components/layout/Block'
|
import Block from '~/components/layout/Block'
|
||||||
import Identicon from '~/components/Identicon'
|
import Identicon from '~/components/Identicon'
|
||||||
import { withStyles } from '@material-ui/core/styles'
|
|
||||||
import Heading from '~/components/layout/Heading'
|
import Heading from '~/components/layout/Heading'
|
||||||
import Row from '~/components/layout/Row'
|
import Row from '~/components/layout/Row'
|
||||||
import Link from '~/components/layout/Link'
|
import Link from '~/components/layout/Link'
|
||||||
|
@ -21,10 +21,14 @@ import { copyToClipboard } from '~/utils/clipboard'
|
||||||
import Balances from './Balances'
|
import Balances from './Balances'
|
||||||
import Settings from './Settings'
|
import Settings from './Settings'
|
||||||
|
|
||||||
|
export const SETTINGS_TAB_BTN_TESTID = 'settings-tab-btn'
|
||||||
|
export const SAFE_VIEW_NAME_HEADING_TESTID = 'safe-name-heading'
|
||||||
|
|
||||||
type Props = SelectorProps & {
|
type Props = SelectorProps & {
|
||||||
classes: Object,
|
classes: Object,
|
||||||
granted: boolean,
|
granted: boolean,
|
||||||
createTransaction: Function,
|
createTransaction: Function,
|
||||||
|
updateSafe: Function,
|
||||||
}
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
@ -90,7 +94,15 @@ class Layout extends React.Component<Props, State> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
safe, provider, network, classes, granted, tokens, activeTokens, createTransaction,
|
safe,
|
||||||
|
provider,
|
||||||
|
network,
|
||||||
|
classes,
|
||||||
|
granted,
|
||||||
|
tokens,
|
||||||
|
activeTokens,
|
||||||
|
createTransaction,
|
||||||
|
updateSafe,
|
||||||
} = this.props
|
} = this.props
|
||||||
const { tabIndex } = this.state
|
const { tabIndex } = this.state
|
||||||
|
|
||||||
|
@ -107,7 +119,7 @@ class Layout extends React.Component<Props, State> {
|
||||||
<Identicon address={address} diameter={50} />
|
<Identicon address={address} diameter={50} />
|
||||||
<Block className={classes.name}>
|
<Block className={classes.name}>
|
||||||
<Row>
|
<Row>
|
||||||
<Heading tag="h2" color="secondary">
|
<Heading tag="h2" color="secondary" testId={SAFE_VIEW_NAME_HEADING_TESTID}>
|
||||||
{name}
|
{name}
|
||||||
</Heading>
|
</Heading>
|
||||||
{!granted && <Block className={classes.readonly}>Read Only</Block>}
|
{!granted && <Block className={classes.readonly}>Read Only</Block>}
|
||||||
|
@ -126,7 +138,7 @@ class Layout extends React.Component<Props, State> {
|
||||||
<Tabs value={tabIndex} onChange={this.handleChange} indicatorColor="secondary" textColor="secondary">
|
<Tabs value={tabIndex} onChange={this.handleChange} indicatorColor="secondary" textColor="secondary">
|
||||||
<Tab label="Balances" />
|
<Tab label="Balances" />
|
||||||
<Tab label="Transactions" />
|
<Tab label="Transactions" />
|
||||||
<Tab label="Settings" />
|
<Tab label="Settings" data-testid={SETTINGS_TAB_BTN_TESTID} />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Row>
|
</Row>
|
||||||
<Hairline color="#c8ced4" />
|
<Hairline color="#c8ced4" />
|
||||||
|
@ -148,6 +160,7 @@ class Layout extends React.Component<Props, State> {
|
||||||
safeAddress={address}
|
safeAddress={address}
|
||||||
safeName={name}
|
safeName={name}
|
||||||
etherScanLink={etherScanLink}
|
etherScanLink={etherScanLink}
|
||||||
|
updateSafe={updateSafe}
|
||||||
threshold={safe.threshold}
|
threshold={safe.threshold}
|
||||||
owners={safe.owners}
|
owners={safe.owners}
|
||||||
createTransaction={createTransaction}
|
createTransaction={createTransaction}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react'
|
||||||
|
import { withStyles } from '@material-ui/core/styles'
|
||||||
|
import Block from '~/components/layout/Block'
|
||||||
|
import Col from '~/components/layout/Col'
|
||||||
|
import Field from '~/components/forms/Field'
|
||||||
|
import { composeValidators, required, minMaxLength } from '~/components/forms/validator'
|
||||||
|
import TextField from '~/components/forms/TextField'
|
||||||
|
import GnoForm from '~/components/forms/GnoForm'
|
||||||
|
import Row from '~/components/layout/Row'
|
||||||
|
import Paragraph from '~/components/layout/Paragraph'
|
||||||
|
import Hairline from '~/components/layout/Hairline'
|
||||||
|
import Button from '~/components/layout/Button'
|
||||||
|
import { sm, boldFont } from '~/theme/variables'
|
||||||
|
import { styles } from './style'
|
||||||
|
|
||||||
|
const controlsStyle = {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
padding: sm,
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveButtonStyle = {
|
||||||
|
marginRight: sm,
|
||||||
|
fontWeight: boldFont,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SAFE_NAME_INPUT_TESTID = 'safe-name-input'
|
||||||
|
export const SAFE_NAME_SUBMIT_BTN_TESTID = 'change-safe-name-btn'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
classes: Object,
|
||||||
|
safeAddress: string,
|
||||||
|
safeName: string,
|
||||||
|
updateSafe: Function,
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChangeSafeName = (props: Props) => {
|
||||||
|
const {
|
||||||
|
classes, safeAddress, safeName, updateSafe,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const handleSubmit = (values) => {
|
||||||
|
updateSafe({ address: safeAddress, name: values.safeName })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<GnoForm onSubmit={handleSubmit}>
|
||||||
|
{() => (
|
||||||
|
<React.Fragment>
|
||||||
|
<Block className={classes.formContainer}>
|
||||||
|
<Paragraph noMargin className={classes.title} size="lg" weight="bolder">
|
||||||
|
Modify Safe name
|
||||||
|
</Paragraph>
|
||||||
|
<Paragraph size="sm">
|
||||||
|
You can change the name of this Safe. This name is only stored locally and never shared with Gnosis or
|
||||||
|
any third parties.
|
||||||
|
</Paragraph>
|
||||||
|
<Block className={classes.root}>
|
||||||
|
<Field
|
||||||
|
name="safeName"
|
||||||
|
component={TextField}
|
||||||
|
type="text"
|
||||||
|
validate={composeValidators(required, minMaxLength(1, 50))}
|
||||||
|
placeholder="Safe name*"
|
||||||
|
text="Safe name*"
|
||||||
|
defaultValue={safeName}
|
||||||
|
testId={SAFE_NAME_INPUT_TESTID}
|
||||||
|
/>
|
||||||
|
</Block>
|
||||||
|
</Block>
|
||||||
|
<Hairline />
|
||||||
|
<Row style={controlsStyle} align="end" grow>
|
||||||
|
<Col end="xs">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
style={saveButtonStyle}
|
||||||
|
size="small"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
testId={SAFE_NAME_SUBMIT_BTN_TESTID}
|
||||||
|
>
|
||||||
|
SAVE
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</GnoForm>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withStyles(styles)(ChangeSafeName)
|
|
@ -0,0 +1,17 @@
|
||||||
|
// @flow
|
||||||
|
import { lg } from '~/theme/variables'
|
||||||
|
|
||||||
|
export const styles = () => ({
|
||||||
|
title: {
|
||||||
|
padding: `${lg} 0 20px`,
|
||||||
|
fontSize: '16px',
|
||||||
|
},
|
||||||
|
formContainer: {
|
||||||
|
padding: '0 20px',
|
||||||
|
minHeight: '369px',
|
||||||
|
},
|
||||||
|
root: {
|
||||||
|
display: 'flex',
|
||||||
|
maxWidth: '460px',
|
||||||
|
},
|
||||||
|
})
|
|
@ -18,7 +18,7 @@ import Link from '~/components/layout/Link'
|
||||||
import Paragraph from '~/components/layout/Paragraph'
|
import Paragraph from '~/components/layout/Paragraph'
|
||||||
import Hairline from '~/components/layout/Hairline'
|
import Hairline from '~/components/layout/Hairline'
|
||||||
import actions, { type Actions } from './actions'
|
import actions, { type Actions } from './actions'
|
||||||
import { lg, md, secondary } from '~/theme/variables'
|
import { secondary } from '~/theme/variables'
|
||||||
import { styles } from './style'
|
import { styles } from './style'
|
||||||
|
|
||||||
const openIconStyle = {
|
const openIconStyle = {
|
||||||
|
@ -36,22 +36,11 @@ type Props = Actions & {
|
||||||
}
|
}
|
||||||
|
|
||||||
const RemoveSafeComponent = ({
|
const RemoveSafeComponent = ({
|
||||||
onClose,
|
onClose, isOpen, classes, safeAddress, etherScanLink, safeName, removeSafe,
|
||||||
isOpen,
|
|
||||||
classes,
|
|
||||||
safeAddress,
|
|
||||||
etherScanLink,
|
|
||||||
safeName,
|
|
||||||
removeSafe,
|
|
||||||
}: Props) => (
|
}: Props) => (
|
||||||
<Modal
|
<Modal title="Remove Safe" description="Remove the selected Safe" handleClose={onClose} open={isOpen}>
|
||||||
title="Remove Safe"
|
|
||||||
description="Remove the selected Safe"
|
|
||||||
handleClose={onClose}
|
|
||||||
open={isOpen}
|
|
||||||
>
|
|
||||||
<Row align="center" grow className={classes.heading}>
|
<Row align="center" grow className={classes.heading}>
|
||||||
<Paragraph className={classes.manage} noMargin>
|
<Paragraph className={classes.manage} noMargin weight="bolder">
|
||||||
Remove Safe
|
Remove Safe
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
<IconButton onClick={onClose} disableRipple>
|
<IconButton onClick={onClose} disableRipple>
|
||||||
|
@ -66,7 +55,7 @@ const RemoveSafeComponent = ({
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={11}>
|
<Col xs={11}>
|
||||||
<Block className={classNames(classes.name, classes.userName)}>
|
<Block className={classNames(classes.name, classes.userName)}>
|
||||||
<Paragraph size="lg" noMargin>
|
<Paragraph size="lg" noMargin weight="bolder">
|
||||||
{safeName}
|
{safeName}
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
<Block align="center" className={classes.user}>
|
<Block align="center" className={classes.user}>
|
||||||
|
@ -84,8 +73,8 @@ const RemoveSafeComponent = ({
|
||||||
<Row className={classes.description}>
|
<Row className={classes.description}>
|
||||||
<Paragraph noMargin>
|
<Paragraph noMargin>
|
||||||
Removing a Safe only removes it from your interface.
|
Removing a Safe only removes it from your interface.
|
||||||
<b>It does not delete the Safe</b>.
|
<b>It does not delete the Safe</b>
|
||||||
You can always add it back using the Safe's address.
|
. You can always add it back using the Safe's address.
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
</Row>
|
</Row>
|
||||||
</Block>
|
</Block>
|
||||||
|
|
|
@ -14,7 +14,6 @@ import ChangeThreshold from './ChangeThreshold'
|
||||||
import type { Owner } from '~/routes/safe/store/models/owner'
|
import type { Owner } from '~/routes/safe/store/models/owner'
|
||||||
import { styles } from './style'
|
import { styles } from './style'
|
||||||
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
||||||
import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses'
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
owners: List<Owner>,
|
owners: List<Owner>,
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
// @flow
|
|
||||||
import * as React from 'react'
|
|
||||||
import { withStyles } from '@material-ui/core/styles'
|
|
||||||
import Block from '~/components/layout/Block'
|
|
||||||
import Col from '~/components/layout/Col'
|
|
||||||
import Row from '~/components/layout/Row'
|
|
||||||
import Paragraph from '~/components/layout/Paragraph'
|
|
||||||
import Button from '~/components/layout/Button'
|
|
||||||
import { sm, boldFont } from '~/theme/variables'
|
|
||||||
import { styles } from './style'
|
|
||||||
|
|
||||||
const controlsStyle = {
|
|
||||||
backgroundColor: 'white',
|
|
||||||
padding: sm,
|
|
||||||
}
|
|
||||||
|
|
||||||
const saveButtonStyle = {
|
|
||||||
marginRight: sm,
|
|
||||||
fontWeight: boldFont,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
classes: Object,
|
|
||||||
}
|
|
||||||
|
|
||||||
class UpdateSafeName extends React.Component<Props, State> {
|
|
||||||
render() {
|
|
||||||
const { classes } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<Block margin="lg">
|
|
||||||
<Paragraph size="lg" color="primary" noMargin>
|
|
||||||
Details
|
|
||||||
</Paragraph>
|
|
||||||
</Block>
|
|
||||||
<Row style={controlsStyle} align="end" grow>
|
|
||||||
<Col end="xs">
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
style={saveButtonStyle}
|
|
||||||
size="small"
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
onClick={() => {}}
|
|
||||||
>
|
|
||||||
SAVE
|
|
||||||
</Button>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withStyles(styles)(UpdateSafeName)
|
|
|
@ -1,4 +0,0 @@
|
||||||
// @flow
|
|
||||||
import { lg, border } from '~/theme/variables'
|
|
||||||
|
|
||||||
export const styles = () => ({})
|
|
|
@ -9,7 +9,8 @@ import Row from '~/components/layout/Row'
|
||||||
import RemoveSafeModal from './RemoveSafeModal'
|
import RemoveSafeModal from './RemoveSafeModal'
|
||||||
import Paragraph from '~/components/layout/Paragraph'
|
import Paragraph from '~/components/layout/Paragraph'
|
||||||
import Hairline from '~/components/layout/Hairline'
|
import Hairline from '~/components/layout/Hairline'
|
||||||
import type { Owner } from '~/routes/safe/store/models/owner'
|
import { type Owner } from '~/routes/safe/store/models/owner'
|
||||||
|
import ChangeSafeName from './ChangeSafeName'
|
||||||
import ThresholdSettings from './ThresholdSettings'
|
import ThresholdSettings from './ThresholdSettings'
|
||||||
import { styles } from './style'
|
import { styles } from './style'
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ type Props = {
|
||||||
owners: List<Owner>,
|
owners: List<Owner>,
|
||||||
threshold: number,
|
threshold: number,
|
||||||
createTransaction: Function,
|
createTransaction: Function,
|
||||||
|
updateSafe: Function,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Action = 'RemoveSafe'
|
type Action = 'RemoveSafe'
|
||||||
|
@ -52,14 +54,24 @@ class Settings extends React.Component<Props, State> {
|
||||||
render() {
|
render() {
|
||||||
const { showRemoveSafe, menuOptionIndex } = this.state
|
const { showRemoveSafe, menuOptionIndex } = this.state
|
||||||
const {
|
const {
|
||||||
classes, granted, etherScanLink, safeAddress, safeName, owners, threshold, createTransaction,
|
classes,
|
||||||
|
granted,
|
||||||
|
etherScanLink,
|
||||||
|
safeAddress,
|
||||||
|
safeName,
|
||||||
|
updateSafe,
|
||||||
|
owners,
|
||||||
|
threshold,
|
||||||
|
createTransaction,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Row align="center" className={classes.message}>
|
<Row align="center" className={classes.message}>
|
||||||
<Col xs={6}>
|
<Col xs={6}>
|
||||||
<Paragraph className={classes.settings}>Settings</Paragraph>
|
<Paragraph className={classes.settings} size="lg" weight="bolder">
|
||||||
|
Settings
|
||||||
|
</Paragraph>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={6} end="sm">
|
<Col xs={6} end="sm">
|
||||||
<Paragraph noMargin size="md" color="error" className={classes.links} onClick={this.onShow('RemoveSafe')}>
|
<Paragraph noMargin size="md" color="error" className={classes.links} onClick={this.onShow('RemoveSafe')}>
|
||||||
|
@ -113,7 +125,9 @@ class Settings extends React.Component<Props, State> {
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={9} layout="column">
|
<Col xs={9} layout="column">
|
||||||
<Block className={classes.container}>
|
<Block className={classes.container}>
|
||||||
{menuOptionIndex === 1 && <p>To be done</p>}
|
{menuOptionIndex === 1 && (
|
||||||
|
<ChangeSafeName safeAddress={safeAddress} safeName={safeName} updateSafe={updateSafe} />
|
||||||
|
)}
|
||||||
{granted && menuOptionIndex === 2 && <p>To be done</p>}
|
{granted && menuOptionIndex === 2 && <p>To be done</p>}
|
||||||
{granted && menuOptionIndex === 3 && (
|
{granted && menuOptionIndex === 3 && (
|
||||||
<ThresholdSettings
|
<ThresholdSettings
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// @flow
|
// @flow
|
||||||
import {
|
import {
|
||||||
sm, md, lg, border, secondary, bolderFont,
|
sm, lg, border, secondary, bolderFont,
|
||||||
} from '~/theme/variables'
|
} from '~/theme/variables'
|
||||||
|
|
||||||
export const styles = (theme: Object) => ({
|
export const styles = () => ({
|
||||||
root: {
|
root: {
|
||||||
backgroundColor: 'white',
|
backgroundColor: 'white',
|
||||||
boxShadow: '0 -1px 4px 0 rgba(74, 85, 121, 0.5)',
|
boxShadow: '0 -1px 4px 0 rgba(74, 85, 121, 0.5)',
|
||||||
|
|
|
@ -2,15 +2,18 @@
|
||||||
import fetchSafe from '~/routes/safe/store/actions/fetchSafe'
|
import fetchSafe from '~/routes/safe/store/actions/fetchSafe'
|
||||||
import fetchTokenBalances from '~/routes/safe/store/actions/fetchTokenBalances'
|
import fetchTokenBalances from '~/routes/safe/store/actions/fetchTokenBalances'
|
||||||
import createTransaction from '~/routes/safe/store/actions/createTransaction'
|
import createTransaction from '~/routes/safe/store/actions/createTransaction'
|
||||||
|
import updateSafe from '~/routes/safe/store/actions/updateSafe'
|
||||||
|
|
||||||
export type Actions = {
|
export type Actions = {
|
||||||
fetchSafe: typeof fetchSafe,
|
fetchSafe: typeof fetchSafe,
|
||||||
fetchTokenBalances: typeof fetchTokenBalances,
|
fetchTokenBalances: typeof fetchTokenBalances,
|
||||||
createTransaction: typeof createTransaction,
|
createTransaction: typeof createTransaction,
|
||||||
|
updateSafe: typeof updateSafe,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
fetchSafe,
|
fetchSafe,
|
||||||
fetchTokenBalances,
|
fetchTokenBalances,
|
||||||
createTransaction,
|
createTransaction,
|
||||||
|
updateSafe,
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,9 @@ class SafeView extends React.Component<Props> {
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
const { activeTokens } = this.props
|
const { activeTokens } = this.props
|
||||||
|
const oldActiveTokensSize = prevProps.activeTokens.size
|
||||||
|
|
||||||
if (activeTokens.size > prevProps.activeTokens.size) {
|
if (oldActiveTokensSize > 0 && activeTokens.size > oldActiveTokensSize) {
|
||||||
this.checkForUpdates()
|
this.checkForUpdates()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +53,15 @@ class SafeView extends React.Component<Props> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
safe, provider, activeTokens, granted, userAddress, network, tokens, createTransaction,
|
safe,
|
||||||
|
provider,
|
||||||
|
activeTokens,
|
||||||
|
granted,
|
||||||
|
userAddress,
|
||||||
|
network,
|
||||||
|
tokens,
|
||||||
|
createTransaction,
|
||||||
|
updateSafe,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -66,6 +75,7 @@ class SafeView extends React.Component<Props> {
|
||||||
network={network}
|
network={network}
|
||||||
granted={granted}
|
granted={granted}
|
||||||
createTransaction={createTransaction}
|
createTransaction={createTransaction}
|
||||||
|
updateSafe={updateSafe}
|
||||||
/>
|
/>
|
||||||
</Page>
|
</Page>
|
||||||
)
|
)
|
||||||
|
|
|
@ -61,6 +61,10 @@ describe('DOM > Feature > LOAD a safe', () => {
|
||||||
fireEvent.submit(form)
|
fireEvent.submit(form)
|
||||||
await sleep(400)
|
await sleep(400)
|
||||||
|
|
||||||
|
// submit form with owners names
|
||||||
|
fireEvent.submit(form)
|
||||||
|
await sleep(400)
|
||||||
|
|
||||||
// Submit
|
// Submit
|
||||||
fireEvent.submit(form)
|
fireEvent.submit(form)
|
||||||
const deployedAddress = await whenSafeDeployed()
|
const deployedAddress = await whenSafeDeployed()
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
// @flow
|
||||||
|
import { fireEvent, cleanup } from '@testing-library/react'
|
||||||
|
import { aNewStore } from '~/store'
|
||||||
|
import { aMinedSafe } from '~/test/builder/safe.redux.builder'
|
||||||
|
import { renderSafeView } from '~/test/builder/safe.dom.utils'
|
||||||
|
import { sleep } from '~/utils/timer'
|
||||||
|
import 'jest-dom/extend-expect'
|
||||||
|
import { SETTINGS_TAB_BTN_TESTID, SAFE_VIEW_NAME_HEADING_TESTID } from '~/routes/safe/components/Layout'
|
||||||
|
import { SAFE_NAME_INPUT_TESTID, SAFE_NAME_SUBMIT_BTN_TESTID } from '~/routes/safe/components/Settings/ChangeSafeName'
|
||||||
|
|
||||||
|
afterEach(cleanup)
|
||||||
|
|
||||||
|
describe('DOM > Feature > Settings', () => {
|
||||||
|
let store
|
||||||
|
let safeAddress
|
||||||
|
beforeEach(async () => {
|
||||||
|
store = aNewStore()
|
||||||
|
// using 4th account because other accounts were used in other tests and paid gas
|
||||||
|
safeAddress = await aMinedSafe(store)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Changes safe name', async () => {
|
||||||
|
const INITIAL_NAME = 'Safe Name'
|
||||||
|
const NEW_NAME = 'NEW SAFE NAME'
|
||||||
|
|
||||||
|
const SafeDom = renderSafeView(store, safeAddress)
|
||||||
|
await sleep(1300)
|
||||||
|
|
||||||
|
const safeNameHeading = SafeDom.getByTestId(SAFE_VIEW_NAME_HEADING_TESTID)
|
||||||
|
expect(safeNameHeading).toHaveTextContent(INITIAL_NAME)
|
||||||
|
|
||||||
|
// Open settings tab
|
||||||
|
// Safe name setting screen should be pre-selected
|
||||||
|
const settingsBtn = SafeDom.getByTestId(SETTINGS_TAB_BTN_TESTID)
|
||||||
|
fireEvent.click(settingsBtn)
|
||||||
|
|
||||||
|
// Change the name
|
||||||
|
const safeNameInput = SafeDom.getByTestId(SAFE_NAME_INPUT_TESTID)
|
||||||
|
const submitBtn = SafeDom.getByTestId(SAFE_NAME_SUBMIT_BTN_TESTID)
|
||||||
|
fireEvent.change(safeNameInput, { target: { value: NEW_NAME } })
|
||||||
|
fireEvent.click(submitBtn)
|
||||||
|
|
||||||
|
// Check if the name changed
|
||||||
|
expect(safeNameHeading).toHaveTextContent(NEW_NAME)
|
||||||
|
})
|
||||||
|
})
|
18765
yarn-error.log
18765
yarn-error.log
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue