mirror of
https://github.com/status-im/safe-react.git
synced 2025-02-25 16:05:25 +00:00
* Adds query-string package.json Parses query string on open layout * Implements load all the values on openSafe view from param querys * Adds query params validation * Moves query parse logic to open.jsx * Changes default no metamask component on open page * Replaces global isNaN * Fix threshold parsing validation * Updates the welcome component with new verbiage for open * Renames isOpenSafe to isOldMultisigMigration * Merge branch 'development' of https://github.com/gnosis/safe-react into 122-multisig-migration # Conflicts: # src/routes/open/components/Layout.jsx * Merge branch 'development' of https://github.com/gnosis/safe-react into 159-pending-transactions # Conflicts: # src/routes/safe/components/Transactions/index.jsx # yarn.lock
This commit is contained in:
parent
f0b3172abe
commit
e7ba5e5392
@ -54,6 +54,7 @@
|
|||||||
"optimize-css-assets-webpack-plugin": "5.0.3",
|
"optimize-css-assets-webpack-plugin": "5.0.3",
|
||||||
"polished": "^3.4.2",
|
"polished": "^3.4.2",
|
||||||
"qrcode.react": "1.0.0",
|
"qrcode.react": "1.0.0",
|
||||||
|
"query-string": "^6.9.0",
|
||||||
"react": "16.12.0",
|
"react": "16.12.0",
|
||||||
"react-dev-utils": "^10.0.0",
|
"react-dev-utils": "^10.0.0",
|
||||||
"react-dom": "16.12.0",
|
"react-dom": "16.12.0",
|
||||||
|
@ -19,6 +19,7 @@ type Props = {
|
|||||||
testId?: string,
|
testId?: string,
|
||||||
validators?: Function[],
|
validators?: Function[],
|
||||||
inputAdornment?: React.Element,
|
inputAdornment?: React.Element,
|
||||||
|
defaultValue?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const isValidEnsName = (name) => /^([\w-]+\.)+(eth|test|xyz|luxe)$/.test(name)
|
const isValidEnsName = (name) => /^([\w-]+\.)+(eth|test|xyz|luxe)$/.test(name)
|
||||||
@ -35,6 +36,7 @@ const AddressInput = ({
|
|||||||
testId,
|
testId,
|
||||||
inputAdornment,
|
inputAdornment,
|
||||||
validators = [],
|
validators = [],
|
||||||
|
defaultValue,
|
||||||
}: Props): React.Element<*> => (
|
}: Props): React.Element<*> => (
|
||||||
<>
|
<>
|
||||||
<Field
|
<Field
|
||||||
@ -51,6 +53,7 @@ const AddressInput = ({
|
|||||||
text={text}
|
text={text}
|
||||||
className={className}
|
className={className}
|
||||||
testId={testId}
|
testId={testId}
|
||||||
|
defaultValue={defaultValue}
|
||||||
/>
|
/>
|
||||||
<OnChange name={name}>
|
<OnChange name={name}>
|
||||||
{async (value) => {
|
{async (value) => {
|
||||||
|
@ -9,23 +9,54 @@ import Row from '~/components/layout/Row'
|
|||||||
import Review from '~/routes/open/components/ReviewInformation'
|
import Review from '~/routes/open/components/ReviewInformation'
|
||||||
import SafeNameField from '~/routes/open/components/SafeNameForm'
|
import SafeNameField from '~/routes/open/components/SafeNameForm'
|
||||||
import SafeOwnersFields from '~/routes/open/components/SafeOwnersConfirmationsForm'
|
import SafeOwnersFields from '~/routes/open/components/SafeOwnersConfirmationsForm'
|
||||||
import { getOwnerNameBy, getOwnerAddressBy, FIELD_CONFIRMATIONS } from '~/routes/open/components/fields'
|
import {
|
||||||
|
getOwnerNameBy,
|
||||||
|
getOwnerAddressBy,
|
||||||
|
FIELD_CONFIRMATIONS,
|
||||||
|
FIELD_SAFE_NAME,
|
||||||
|
} from '~/routes/open/components/fields'
|
||||||
import { history } from '~/store'
|
import { history } from '~/store'
|
||||||
import { secondary, sm } from '~/theme/variables'
|
import { secondary, sm } from '~/theme/variables'
|
||||||
|
import type { SafePropsType } from '~/routes/open/container/Open'
|
||||||
|
import Welcome from '~/routes/welcome/components/Layout'
|
||||||
|
|
||||||
const getSteps = () => ['Name', 'Owners and confirmations', 'Review']
|
const getSteps = () => ['Name', 'Owners and confirmations', 'Review']
|
||||||
|
|
||||||
const initialValuesFrom = (userAccount: string) => ({
|
|
||||||
|
const initialValuesFrom = (userAccount: string, safeProps?: SafePropsType) => {
|
||||||
|
if (!safeProps) {
|
||||||
|
return ({
|
||||||
[getOwnerNameBy(0)]: 'My Wallet',
|
[getOwnerNameBy(0)]: 'My Wallet',
|
||||||
[getOwnerAddressBy(0)]: userAccount,
|
[getOwnerAddressBy(0)]: userAccount,
|
||||||
[FIELD_CONFIRMATIONS]: '1',
|
[FIELD_CONFIRMATIONS]: '1',
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
let obj = {}
|
||||||
|
const {
|
||||||
|
ownerAddresses, ownerNames, threshold, name,
|
||||||
|
} = safeProps
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const [index, value] of ownerAddresses.entries()) {
|
||||||
|
const safeName = ownerNames[index] ? ownerNames[index] : 'My Wallet'
|
||||||
|
obj = {
|
||||||
|
...obj,
|
||||||
|
[getOwnerAddressBy(index)]: value,
|
||||||
|
[getOwnerNameBy(index)]: safeName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ({
|
||||||
|
...obj,
|
||||||
|
[FIELD_CONFIRMATIONS]: threshold || '1',
|
||||||
|
[FIELD_SAFE_NAME]: name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
provider: string,
|
provider: string,
|
||||||
userAccount: string,
|
userAccount: string,
|
||||||
network: string,
|
network: string,
|
||||||
onCallSafeContractSubmit: (values: Object) => Promise<void>,
|
onCallSafeContractSubmit: (values: Object) => Promise<void>,
|
||||||
|
safeProps?: SafePropsType,
|
||||||
}
|
}
|
||||||
|
|
||||||
const iconStyle = {
|
const iconStyle = {
|
||||||
@ -44,11 +75,14 @@ const formMutators = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const Layout = ({
|
|
||||||
provider, userAccount, onCallSafeContractSubmit, network,
|
const Layout = (props: Props) => {
|
||||||
}: Props) => {
|
const {
|
||||||
|
provider, userAccount, onCallSafeContractSubmit, network, safeProps,
|
||||||
|
} = props
|
||||||
const steps = getSteps()
|
const steps = getSteps()
|
||||||
const initialValues = initialValuesFrom(userAccount)
|
|
||||||
|
const initialValues = initialValuesFrom(userAccount, safeProps)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -75,7 +109,7 @@ const Layout = ({
|
|||||||
</Stepper>
|
</Stepper>
|
||||||
</Block>
|
</Block>
|
||||||
) : (
|
) : (
|
||||||
<div>No web3 provider detected</div>
|
<Welcome provider={provider} isOldMultisigMigration />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -12,6 +12,7 @@ import { sm, secondary } from '~/theme/variables'
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
classes: Object,
|
classes: Object,
|
||||||
|
safeName?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = () => ({
|
const styles = () => ({
|
||||||
@ -32,11 +33,12 @@ const styles = () => ({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const SafeName = ({ classes }: Props) => (
|
const SafeName = ({ classes, safeName }: Props) => (
|
||||||
<>
|
<>
|
||||||
<Block margin="lg">
|
<Block margin="lg">
|
||||||
<Paragraph noMargin size="md" color="primary">
|
<Paragraph noMargin size="md" color="primary">
|
||||||
You are about to create a new Gnosis Safe wallet with one or more owners. First, let's give your new wallet
|
You are about to create a new Gnosis Safe wallet with one or more owners. First, let's give your new
|
||||||
|
wallet
|
||||||
a name. This name is only stored locally and will never be shared with Gnosis or any third parties.
|
a name. This name is only stored locally and will never be shared with Gnosis or any third parties.
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
</Block>
|
</Block>
|
||||||
@ -48,6 +50,7 @@ const SafeName = ({ classes }: Props) => (
|
|||||||
validate={required}
|
validate={required}
|
||||||
placeholder="Name of the new Safe"
|
placeholder="Name of the new Safe"
|
||||||
text="Safe name"
|
text="Safe name"
|
||||||
|
defaultValue={safeName}
|
||||||
/>
|
/>
|
||||||
</Block>
|
</Block>
|
||||||
<Block margin="lg">
|
<Block margin="lg">
|
||||||
@ -72,10 +75,13 @@ const SafeName = ({ classes }: Props) => (
|
|||||||
|
|
||||||
const SafeNameForm = withStyles(styles)(SafeName)
|
const SafeNameForm = withStyles(styles)(SafeName)
|
||||||
|
|
||||||
const SafeNamePage = () => (controls: React.Node) => (
|
const SafeNamePage = () => (controls: React.Node, { values }) => {
|
||||||
|
const { safeName } = values
|
||||||
|
return (
|
||||||
<OpenPaper controls={controls}>
|
<OpenPaper controls={controls}>
|
||||||
<SafeNameForm />
|
<SafeNameForm safeName={safeName} />
|
||||||
</OpenPaper>
|
</OpenPaper>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default SafeNamePage
|
export default SafeNamePage
|
||||||
|
@ -4,6 +4,7 @@ import { withStyles } from '@material-ui/core/styles'
|
|||||||
import InputAdornment from '@material-ui/core/InputAdornment'
|
import InputAdornment from '@material-ui/core/InputAdornment'
|
||||||
import CheckCircle from '@material-ui/icons/CheckCircle'
|
import CheckCircle from '@material-ui/icons/CheckCircle'
|
||||||
import MenuItem from '@material-ui/core/MenuItem'
|
import MenuItem from '@material-ui/core/MenuItem'
|
||||||
|
import { withRouter } from 'react-router-dom'
|
||||||
import Field from '~/components/forms/Field'
|
import Field from '~/components/forms/Field'
|
||||||
import TextField from '~/components/forms/TextField'
|
import TextField from '~/components/forms/TextField'
|
||||||
import SelectField from '~/components/forms/SelectField'
|
import SelectField from '~/components/forms/SelectField'
|
||||||
@ -70,6 +71,7 @@ const SafeOwners = (props: Props) => {
|
|||||||
} = props
|
} = props
|
||||||
|
|
||||||
const validOwners = getNumOwnersFrom(values)
|
const validOwners = getNumOwnersFrom(values)
|
||||||
|
|
||||||
const [numOwners, setNumOwners] = useState<number>(validOwners)
|
const [numOwners, setNumOwners] = useState<number>(validOwners)
|
||||||
const [qrModalOpen, setQrModalOpen] = useState<boolean>(false)
|
const [qrModalOpen, setQrModalOpen] = useState<boolean>(false)
|
||||||
const [scanQrForOwnerName, setScanQrForOwnerName] = useState<string | null>(null)
|
const [scanQrForOwnerName, setScanQrForOwnerName] = useState<string | null>(null)
|
||||||
@ -222,7 +224,7 @@ owner(s)
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const SafeOwnersForm = withStyles(styles)(SafeOwners)
|
const SafeOwnersForm = withStyles(styles)(withRouter(SafeOwners))
|
||||||
|
|
||||||
const SafeOwnersPage = ({ updateInitialProps }: Object) => (controls: React.Node, { values, errors, form }: Object) => (
|
const SafeOwnersPage = ({ updateInitialProps }: Object) => (controls: React.Node, { values, errors, form }: Object) => (
|
||||||
<>
|
<>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
export const FIELD_NAME: string = 'name'
|
export const FIELD_NAME: string = 'name'
|
||||||
export const FIELD_CONFIRMATIONS: string = 'confirmations'
|
export const FIELD_CONFIRMATIONS: string = 'confirmations'
|
||||||
export const FIELD_OWNERS: string = 'owners'
|
export const FIELD_OWNERS: string = 'owners'
|
||||||
|
export const FIELD_SAFE_NAME: string = 'safeName'
|
||||||
|
|
||||||
export const getOwnerNameBy = (index: number) => `owner${index}Name`
|
export const getOwnerNameBy = (index: number) => `owner${index}Name`
|
||||||
export const getOwnerAddressBy = (index: number) => `owner${index}Address`
|
export const getOwnerAddressBy = (index: number) => `owner${index}Address`
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
|
import queryString from 'query-string'
|
||||||
|
import { withRouter } from 'react-router-dom'
|
||||||
import Page from '~/components/layout/Page'
|
import Page from '~/components/layout/Page'
|
||||||
import {
|
import {
|
||||||
getAccountsFrom, getThresholdFrom, getNamesFrom, getSafeNameFrom, getOwnersFrom,
|
getAccountsFrom, getThresholdFrom, getNamesFrom, getSafeNameFrom, getOwnersFrom,
|
||||||
@ -24,6 +26,30 @@ export type OpenState = {
|
|||||||
safeAddress: string,
|
safeAddress: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SafePropsType = {
|
||||||
|
name: string,
|
||||||
|
ownerAddresses: string[],
|
||||||
|
ownerNames: string[],
|
||||||
|
threshold: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
const validateQueryParams = (ownerAddresses?: string[], ownerNames?: string[], threshold?: string, safeName?: string) => {
|
||||||
|
if (!ownerAddresses || !ownerNames || !threshold || !safeName) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!ownerAddresses.length === 0 || ownerNames.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Number.isNaN(Number(threshold))) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (threshold > ownerAddresses.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
export const createSafe = async (values: Object, userAccount: string, addSafe: AddSafe): Promise<OpenState> => {
|
export const createSafe = async (values: Object, userAccount: string, addSafe: AddSafe): Promise<OpenState> => {
|
||||||
const numConfirmations = getThresholdFrom(values)
|
const numConfirmations = getThresholdFrom(values)
|
||||||
const name = getSafeNameFrom(values)
|
const name = getSafeNameFrom(values)
|
||||||
@ -75,8 +101,23 @@ class Open extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { provider, userAccount, network } = this.props
|
const {
|
||||||
|
provider, userAccount, network, location,
|
||||||
|
} = this.props
|
||||||
|
const query: SafePropsType = queryString.parse(location.search, { arrayFormat: 'comma' })
|
||||||
|
const {
|
||||||
|
name, owneraddresses, ownernames, threshold,
|
||||||
|
} = query
|
||||||
|
|
||||||
|
let safeProps = null
|
||||||
|
if (validateQueryParams(owneraddresses, ownernames, threshold, name)) {
|
||||||
|
safeProps = {
|
||||||
|
name,
|
||||||
|
ownerAddresses: owneraddresses,
|
||||||
|
ownerNames: ownernames,
|
||||||
|
threshold,
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<Layout
|
<Layout
|
||||||
@ -84,10 +125,11 @@ class Open extends React.Component<Props> {
|
|||||||
provider={provider}
|
provider={provider}
|
||||||
userAccount={userAccount}
|
userAccount={userAccount}
|
||||||
onCallSafeContractSubmit={this.onCallSafeContractSubmit}
|
onCallSafeContractSubmit={this.onCallSafeContractSubmit}
|
||||||
|
safeProps={safeProps}
|
||||||
/>
|
/>
|
||||||
</Page>
|
</Page>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(selector, actions)(Open)
|
export default connect(selector, actions)(withRouter(Open))
|
||||||
|
@ -15,7 +15,8 @@ const safe = require('../assets/safe.svg')
|
|||||||
const plus = require('../assets/new.svg')
|
const plus = require('../assets/new.svg')
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
provider: string
|
provider: string,
|
||||||
|
isOldMultisigMigration?: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
const openIconStyle = {
|
const openIconStyle = {
|
||||||
@ -64,14 +65,20 @@ export const LoadSafe = ({ size, provider }: SafeProps) => (
|
|||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
|
|
||||||
const Welcome = ({ provider }: Props) => (
|
|
||||||
<Block className={styles.safe}>
|
const Welcome = ({ provider, isOldMultisigMigration }: Props) => {
|
||||||
<Heading tag="h1" weight="bold" align="center" margin="lg">
|
const headingText = isOldMultisigMigration ? (
|
||||||
Welcome to
|
<>
|
||||||
|
We will replicate the owner structure from your existing Gnosis Multisig
|
||||||
<br />
|
<br />
|
||||||
Gnosis Safe For Teams
|
to let you test the new interface.
|
||||||
</Heading>
|
<br />
|
||||||
<Heading tag="h3" align="center" margin="xl">
|
As soon as you feel comfortable, start moving funds to your new Safe.
|
||||||
|
<br />
|
||||||
|
{' '}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
Gnosis Safe for Teams is the most secure way to manage crypto funds
|
Gnosis Safe for Teams is the most secure way to manage crypto funds
|
||||||
<br />
|
<br />
|
||||||
collectively. It is an improvement of the Gnosis MultiSig, which is used
|
collectively. It is an improvement of the Gnosis MultiSig, which is used
|
||||||
@ -85,6 +92,17 @@ const Welcome = ({ provider }: Props) => (
|
|||||||
design, formally verified smart contracts and vastly improved user
|
design, formally verified smart contracts and vastly improved user
|
||||||
experience.
|
experience.
|
||||||
{' '}
|
{' '}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<Block className={styles.safe}>
|
||||||
|
<Heading tag="h1" weight="bold" align="center" margin="lg">
|
||||||
|
Welcome to
|
||||||
|
<br />
|
||||||
|
Gnosis Safe For Teams
|
||||||
|
</Heading>
|
||||||
|
<Heading tag="h3" align="center" margin="xl">
|
||||||
|
{ headingText }
|
||||||
<a
|
<a
|
||||||
className={styles.learnMoreLink}
|
className={styles.learnMoreLink}
|
||||||
href="https://safe.gnosis.io/teams"
|
href="https://safe.gnosis.io/teams"
|
||||||
@ -113,6 +131,7 @@ const Welcome = ({ provider }: Props) => (
|
|||||||
</Block>
|
</Block>
|
||||||
)}
|
)}
|
||||||
</Block>
|
</Block>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default Welcome
|
export default Welcome
|
||||||
|
Loading…
x
Reference in New Issue
Block a user