Refactor stepper to hooks
This commit is contained in:
parent
25eadc369d
commit
f9c67171a8
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Page from '~/components/layout/Page'
|
||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||
import Page from '~/components/layout/Page'
|
||||
|
||||
const centerStyle = {
|
||||
margin: 'auto 0',
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Stepper from '@material-ui/core/Stepper'
|
||||
import FormStep from '@material-ui/core/Step'
|
||||
import StepLabel from '@material-ui/core/StepLabel'
|
||||
import StepContent from '@material-ui/core/StepContent'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import * as React from 'react'
|
||||
import GnoForm from '~/components/forms/GnoForm'
|
||||
import Hairline from '~/components/layout/Hairline'
|
||||
import Button from '~/components/layout/Button'
|
||||
import { history } from '~/store'
|
||||
import Controls from './Controls'
|
||||
|
||||
const { useState, useEffect } = React
|
||||
|
||||
export { default as Step } from './Step'
|
||||
|
||||
type Props = {
|
||||
|
@ -18,20 +19,14 @@ type Props = {
|
|||
onSubmit: (values: Object) => Promise<void>,
|
||||
children: React.Node,
|
||||
classes: Object,
|
||||
onReset?: () => void,
|
||||
initialValues?: Object,
|
||||
disabledWhenValidating?: boolean,
|
||||
testId?: string,
|
||||
}
|
||||
|
||||
type State = {
|
||||
page: number,
|
||||
values: Object,
|
||||
}
|
||||
|
||||
type PageProps = {
|
||||
children: Function,
|
||||
prepareNextInitialProps: (values: Object) => {},
|
||||
prepareNextInitialProps?: (values: Object) => {},
|
||||
}
|
||||
|
||||
const transitionProps = {
|
||||
|
@ -41,151 +36,117 @@ const transitionProps = {
|
|||
},
|
||||
}
|
||||
|
||||
class GnoStepper extends React.PureComponent<Props, State> {
|
||||
static Page = ({ children }: PageProps) => children
|
||||
export const StepperPage = ({ children }: PageProps) => children
|
||||
|
||||
static FinishButton = ({
|
||||
component, to, title, ...props
|
||||
}) => (
|
||||
<Button component={component} to={to} variant="contained" color="primary" {...props}>
|
||||
{title}
|
||||
</Button>
|
||||
)
|
||||
const GnoStepper = (props: Props) => {
|
||||
const [page, setPage] = useState<number>(0)
|
||||
const [values, setValues] = useState<Object>({})
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
page: 0,
|
||||
values: props.initialValues || {},
|
||||
useEffect(() => {
|
||||
if (props.initialValues) {
|
||||
setValues(props.initialValues)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const getPageProps = (pages: React.Node): PageProps => React.Children.toArray(pages)[page].props
|
||||
|
||||
const updateInitialProps = (newInitialProps) => {
|
||||
setValues(newInitialProps)
|
||||
}
|
||||
|
||||
onReset = () => {
|
||||
const { onReset, initialValues } = this.props
|
||||
if (onReset) {
|
||||
onReset()
|
||||
}
|
||||
const getActivePageFrom = (pages: React.Node) => {
|
||||
const activePageProps = getPageProps(pages)
|
||||
const { children, ...restProps } = activePageProps
|
||||
|
||||
this.setState(() => ({
|
||||
page: 0,
|
||||
values: initialValues || {},
|
||||
}))
|
||||
return children({ ...restProps, updateInitialProps })
|
||||
}
|
||||
|
||||
getPageProps = (pages: React.Node): PageProps => {
|
||||
const { page } = this.state
|
||||
|
||||
return React.Children.toArray(pages)[page].props
|
||||
}
|
||||
|
||||
getActivePageFrom = (pages: React.Node) => {
|
||||
const activePageProps = this.getPageProps(pages)
|
||||
const { children, ...props } = activePageProps
|
||||
|
||||
return children({ ...props, updateInitialProps: this.updateInitialProps })
|
||||
}
|
||||
|
||||
updateInitialProps = (values) => {
|
||||
this.setState({ values })
|
||||
}
|
||||
|
||||
validate = (values: Object) => {
|
||||
const { children } = this.props
|
||||
const { page } = this.state
|
||||
const validate = (valuesToValidate: Object) => {
|
||||
const { children } = props
|
||||
|
||||
const activePage = React.Children.toArray(children)[page]
|
||||
return activePage.props.validate ? activePage.props.validate(values) : {}
|
||||
return activePage.props.validate ? activePage.props.validate(valuesToValidate) : {}
|
||||
}
|
||||
|
||||
next = async (values: Object) => {
|
||||
const { children } = this.props
|
||||
const activePageProps = this.getPageProps(children)
|
||||
const next = async (formValues: Object) => {
|
||||
const { children } = props
|
||||
const activePageProps = getPageProps(children)
|
||||
const { prepareNextInitialProps } = activePageProps
|
||||
|
||||
let pageInitialProps
|
||||
if (prepareNextInitialProps) {
|
||||
pageInitialProps = await prepareNextInitialProps(values)
|
||||
pageInitialProps = await prepareNextInitialProps(formValues)
|
||||
}
|
||||
|
||||
const finalValues = { ...values, ...pageInitialProps }
|
||||
this.setState(state => ({
|
||||
page: Math.min(state.page + 1, React.Children.count(children) - 1),
|
||||
values: finalValues,
|
||||
}))
|
||||
const finalValues = { ...formValues, ...pageInitialProps }
|
||||
|
||||
setValues(finalValues)
|
||||
setPage(Math.min(page + 1, React.Children.count(children) - 1))
|
||||
}
|
||||
|
||||
previous = () => {
|
||||
const { page } = this.state
|
||||
|
||||
const previous = () => {
|
||||
const firstPage = page === 0
|
||||
if (firstPage) {
|
||||
return history.goBack()
|
||||
}
|
||||
|
||||
return this.setState(state => ({
|
||||
page: Math.max(state.page - 1, 0),
|
||||
}))
|
||||
return setPage(Math.max(page - 1, 0))
|
||||
}
|
||||
|
||||
handleSubmit = async (values: Object) => {
|
||||
const { children, onSubmit } = this.props
|
||||
const { page } = this.state
|
||||
const handleSubmit = async (formValues: Object) => {
|
||||
const { children, onSubmit } = props
|
||||
const isLastPage = page === React.Children.count(children) - 1
|
||||
if (isLastPage) {
|
||||
return onSubmit(values)
|
||||
return onSubmit(formValues)
|
||||
}
|
||||
|
||||
return this.next(values)
|
||||
return next(formValues)
|
||||
}
|
||||
|
||||
isLastPage = (page) => {
|
||||
const { steps } = this.props
|
||||
return page === steps.length - 1
|
||||
const isLastPage = (pageNumber) => {
|
||||
const { steps } = props
|
||||
return pageNumber === steps.length - 1
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
steps, children, classes, disabledWhenValidating = false, testId,
|
||||
} = this.props
|
||||
const { page, values } = this.state
|
||||
const activePage = this.getActivePageFrom(children)
|
||||
const lastPage = this.isLastPage(page)
|
||||
const penultimate = this.isLastPage(page + 1)
|
||||
const {
|
||||
steps, children, classes, disabledWhenValidating = false, testId,
|
||||
} = props
|
||||
const activePage = getActivePageFrom(children)
|
||||
const lastPage = isLastPage(page)
|
||||
const penultimate = isLastPage(page + 1)
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<GnoForm onSubmit={this.handleSubmit} initialValues={values} validation={this.validate} testId={testId}>
|
||||
{(submitting: boolean, validating: boolean, ...rest: any) => {
|
||||
const disabled = disabledWhenValidating ? submitting || validating : submitting
|
||||
const controls = (
|
||||
<React.Fragment>
|
||||
<Hairline />
|
||||
<Controls
|
||||
disabled={disabled}
|
||||
onPrevious={this.previous}
|
||||
firstPage={page === 0}
|
||||
lastPage={lastPage}
|
||||
penultimate={penultimate}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
return (
|
||||
<React.Fragment>
|
||||
<GnoForm onSubmit={handleSubmit} initialValues={values} validation={validate} testId={testId}>
|
||||
{(submitting: boolean, validating: boolean, ...rest: any) => {
|
||||
const disabled = disabledWhenValidating ? submitting || validating : submitting
|
||||
const controls = (
|
||||
<React.Fragment>
|
||||
<Hairline />
|
||||
<Controls
|
||||
disabled={disabled}
|
||||
onPrevious={previous}
|
||||
firstPage={page === 0}
|
||||
lastPage={lastPage}
|
||||
penultimate={penultimate}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
||||
return (
|
||||
<Stepper classes={{ root: classes.root }} activeStep={page} orientation="vertical">
|
||||
{steps.map(label => (
|
||||
<FormStep key={label}>
|
||||
<StepLabel>{label}</StepLabel>
|
||||
<StepContent TransitionProps={transitionProps}>{activePage(controls, ...rest)}</StepContent>
|
||||
</FormStep>
|
||||
))}
|
||||
</Stepper>
|
||||
)
|
||||
}}
|
||||
</GnoForm>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Stepper classes={{ root: classes.root }} activeStep={page} orientation="vertical">
|
||||
{steps.map(label => (
|
||||
<FormStep key={label}>
|
||||
<StepLabel>{label}</StepLabel>
|
||||
<StepContent TransitionProps={transitionProps}>{activePage(controls, ...rest)}</StepContent>
|
||||
</FormStep>
|
||||
))}
|
||||
</Stepper>
|
||||
)
|
||||
}}
|
||||
</GnoForm>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import * as React from 'react'
|
||||
import ChevronLeft from '@material-ui/icons/ChevronLeft'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import Stepper, { StepperPage } from '~/components/Stepper'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import Row from '~/components/layout/Row'
|
||||
|
@ -46,11 +46,11 @@ const Layout = ({
|
|||
<Heading tag="h2">Load existing Safe</Heading>
|
||||
</Row>
|
||||
<Stepper onSubmit={onLoadSafeSubmit} steps={steps} initialValues={initialValues} testId="load-safe-form">
|
||||
<Stepper.Page validate={safeFieldsValidation}>{DetailsForm}</Stepper.Page>
|
||||
<Stepper.Page network={network}>{OwnerList}</Stepper.Page>
|
||||
<Stepper.Page network={network} userAddress={userAddress}>
|
||||
<StepperPage validate={safeFieldsValidation}>{DetailsForm}</StepperPage>
|
||||
<StepperPage network={network}>{OwnerList}</StepperPage>
|
||||
<StepperPage network={network} userAddress={userAddress}>
|
||||
{ReviewInformation}
|
||||
</Stepper.Page>
|
||||
</StepperPage>
|
||||
</Stepper>
|
||||
</Block>
|
||||
) : (
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import * as React from 'react'
|
||||
import ChevronLeft from '@material-ui/icons/ChevronLeft'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import Stepper, { StepperPage } from '~/components/Stepper'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import Row from '~/components/layout/Row'
|
||||
|
@ -60,9 +60,9 @@ const Layout = ({
|
|||
initialValues={initialValues}
|
||||
testId="create-safe-form"
|
||||
>
|
||||
<Stepper.Page>{SafeNameField}</Stepper.Page>
|
||||
<Stepper.Page>{SafeOwnersFields}</Stepper.Page>
|
||||
<Stepper.Page network={network}>{Review}</Stepper.Page>
|
||||
<StepperPage>{SafeNameField}</StepperPage>
|
||||
<StepperPage>{SafeOwnersFields}</StepperPage>
|
||||
<StepperPage network={network}>{Review}</StepperPage>
|
||||
</Stepper>
|
||||
</Block>
|
||||
) : (
|
||||
|
|
|
@ -15,7 +15,9 @@ import Button from '~/components/layout/Button'
|
|||
import Row from '~/components/layout/Row'
|
||||
import Img from '~/components/layout/Img'
|
||||
import Col from '~/components/layout/Col'
|
||||
import { FIELD_CONFIRMATIONS, getOwnerNameBy, getOwnerAddressBy, getNumOwnersFrom } from '~/routes/open/components/fields'
|
||||
import {
|
||||
FIELD_CONFIRMATIONS, getOwnerNameBy, getOwnerAddressBy, getNumOwnersFrom,
|
||||
} from '~/routes/open/components/fields'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import { getAccountsFrom } from '~/routes/open/utils/safeDataExtractor'
|
||||
|
@ -183,8 +185,8 @@ const SafeOwnersPage = ({ updateInitialProps }: Object) => (controls: React.Node
|
|||
errors={errors}
|
||||
updateInitialProps={updateInitialProps}
|
||||
values={values}
|
||||
/>
|
||||
{console.log('vals one level up', values)}
|
||||
/>
|
||||
{console.log('vals one level up', values)}
|
||||
</OpenPaper>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
|
|
@ -14,4 +14,4 @@ export const getAddressValidators = (addresses: string[], position: number) => {
|
|||
copy.pop()
|
||||
|
||||
return composeValidators(required, mustBeEthereumAddress, uniqueAddress(copy))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Bold from '~/components/layout/Bold'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import { TKN_DESTINATION_PARAM, TKN_VALUE_PARAM } from '~/routes/safe/components/SendToken/SendTokenForm/index'
|
||||
|
||||
type FormProps = {
|
||||
values: Object,
|
||||
submitting: boolean,
|
||||
}
|
||||
|
||||
type Props = {
|
||||
symbol: string,
|
||||
}
|
||||
|
||||
const spinnerStyle = {
|
||||
minHeight: '50px',
|
||||
}
|
||||
|
||||
const ReviewTx = ({ symbol }: Props) => (controls: React.Node, { values, submitting }: FormProps) => (
|
||||
<OpenPaper controls={controls}>
|
||||
<Heading tag="h2">Review the move token funds</Heading>
|
||||
<Paragraph align="left">
|
||||
<Bold>Destination: </Bold>
|
||||
{' '}
|
||||
{values[TKN_DESTINATION_PARAM]}
|
||||
</Paragraph>
|
||||
<Paragraph align="left">
|
||||
<Bold>{`Amount to transfer: ${values[TKN_VALUE_PARAM]} ${symbol}`}</Bold>
|
||||
</Paragraph>
|
||||
<Block style={spinnerStyle}>{submitting && <CircularProgress size={50} />}</Block>
|
||||
</OpenPaper>
|
||||
)
|
||||
|
||||
export default ReviewTx
|
|
@ -1,53 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Field from '~/components/forms/Field'
|
||||
import TextField from '~/components/forms/TextField'
|
||||
import {
|
||||
composeValidators, inLimit, mustBeFloat, required, greaterThan, mustBeEthereumAddress,
|
||||
} from '~/components/forms/validator'
|
||||
import Block from '~/components/layout/Block'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
|
||||
export const CONFIRMATIONS_ERROR = 'Number of confirmations can not be higher than the number of owners'
|
||||
|
||||
export const TKN_DESTINATION_PARAM = 'tknDestination'
|
||||
export const TKN_VALUE_PARAM = 'tknValue'
|
||||
|
||||
type Props = {
|
||||
funds: string,
|
||||
symbol: string,
|
||||
}
|
||||
|
||||
const SendTokenForm = ({ funds, symbol }: Props) => (controls: React.Node) => (
|
||||
<OpenPaper controls={controls}>
|
||||
<Heading tag="h2" margin="lg">
|
||||
Send tokens Transaction
|
||||
</Heading>
|
||||
<Heading tag="h4" margin="lg">
|
||||
{`Available tokens: ${funds} ${symbol}`}
|
||||
</Heading>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={TKN_DESTINATION_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={composeValidators(required, mustBeEthereumAddress)}
|
||||
placeholder="Destination*"
|
||||
text="Destination"
|
||||
/>
|
||||
</Block>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={TKN_VALUE_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={composeValidators(required, mustBeFloat, greaterThan(0), inLimit(Number(funds), 0, 'available balance', symbol))}
|
||||
placeholder="Amount of tokens*"
|
||||
text="Amount of Tokens"
|
||||
/>
|
||||
</Block>
|
||||
</OpenPaper>
|
||||
)
|
||||
|
||||
export default SendTokenForm
|
|
@ -1,10 +0,0 @@
|
|||
// @flow
|
||||
import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions'
|
||||
|
||||
export type Actions = {
|
||||
fetchTransactions: typeof fetchTransactions,
|
||||
}
|
||||
|
||||
export default {
|
||||
fetchTransactions,
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { BigNumber } from 'bignumber.js'
|
||||
import { connect } from 'react-redux'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import { sleep } from '~/utils/timer'
|
||||
import { type Safe } from '~/routes/safe/store/models/safe'
|
||||
import { getStandardTokenContract } from '~/logic/tokens/store/actions/fetchTokens'
|
||||
import { type Token } from '~/logic/tokens/store/model/token'
|
||||
import { isEther } from '~/logic/tokens/utils/tokenHelpers'
|
||||
import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
|
||||
import { toNative } from '~/logic/wallets/tokens'
|
||||
import { createTransaction } from '~/logic/safe/safeFrontendOperations'
|
||||
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
||||
import actions, { type Actions } from './actions'
|
||||
import selector, { type SelectorProps } from './selector'
|
||||
import SendTokenForm, { TKN_DESTINATION_PARAM, TKN_VALUE_PARAM } from './SendTokenForm'
|
||||
import ReviewTx from './ReviewTx'
|
||||
|
||||
const getSteps = () => ['Fill Move Token form', 'Review Move Token form']
|
||||
|
||||
type Props = SelectorProps &
|
||||
Actions & {
|
||||
safe: Safe,
|
||||
token: Token,
|
||||
onReset: () => void,
|
||||
}
|
||||
|
||||
type State = {
|
||||
done: boolean,
|
||||
}
|
||||
|
||||
export const SEE_TXS_BUTTON_TEXT = 'VISIT TXS'
|
||||
|
||||
const getTransferData = async (tokenAddress: string, to: string, amount: BigNumber) => {
|
||||
const StandardToken = await getStandardTokenContract()
|
||||
const myToken = await StandardToken.at(tokenAddress)
|
||||
|
||||
return myToken.contract.transfer(to, amount).encodeABI()
|
||||
}
|
||||
|
||||
const processTokenTransfer = async (safe: Safe, token: Token, to: string, amount: string, userAddress: string) => {
|
||||
const safeAddress = safe.get('address')
|
||||
const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
|
||||
const nonce = await gnosisSafe.nonce()
|
||||
const symbol = token.get('symbol')
|
||||
const name = `Send ${amount} ${symbol} to ${to}`
|
||||
const value = isEther(symbol) ? amount : '0'
|
||||
const tokenAddress = token.get('address')
|
||||
const destination = isEther(symbol) ? to : tokenAddress
|
||||
const data = isEther(symbol)
|
||||
? EMPTY_DATA
|
||||
: await getTransferData(tokenAddress, to, toNative(amount, token.get('decimals')))
|
||||
|
||||
return createTransaction(safe, name, destination, value, nonce, userAddress, data)
|
||||
}
|
||||
|
||||
class SendToken extends React.Component<Props, State> {
|
||||
state = {
|
||||
done: false,
|
||||
}
|
||||
|
||||
onTransaction = async (values: Object) => {
|
||||
try {
|
||||
const {
|
||||
safe, token, userAddress, fetchTransactions,
|
||||
} = this.props
|
||||
|
||||
const amount = values[TKN_VALUE_PARAM]
|
||||
const destination = values[TKN_DESTINATION_PARAM]
|
||||
|
||||
await processTokenTransfer(safe, token, destination, amount, userAddress)
|
||||
await sleep(1500)
|
||||
fetchTransactions(safe.get('address'))
|
||||
this.setState({ done: true })
|
||||
} catch (error) {
|
||||
this.setState({ done: false })
|
||||
// eslint-disable-next-line
|
||||
console.log('Error while moving ERC20 token funds ' + error)
|
||||
}
|
||||
}
|
||||
|
||||
onReset = () => {
|
||||
const { onReset } = this.props
|
||||
|
||||
this.setState({ done: false })
|
||||
onReset() // This is for show the TX list component
|
||||
}
|
||||
|
||||
render() {
|
||||
const { done } = this.state
|
||||
const { token } = this.props
|
||||
const steps = getSteps()
|
||||
const finishedButton = <Stepper.FinishButton title={SEE_TXS_BUTTON_TEXT} />
|
||||
const symbol = token.get('symbol')
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Stepper
|
||||
finishedTransaction={done}
|
||||
finishedButton={finishedButton}
|
||||
onSubmit={this.onTransaction}
|
||||
steps={steps}
|
||||
onReset={this.onReset}
|
||||
>
|
||||
<Stepper.Page funds={token.get('funds')} symbol={symbol}>
|
||||
{SendTokenForm}
|
||||
</Stepper.Page>
|
||||
<Stepper.Page symbol={symbol}>{ReviewTx}</Stepper.Page>
|
||||
</Stepper>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
selector,
|
||||
actions,
|
||||
)(SendToken)
|
|
@ -1,11 +0,0 @@
|
|||
// @flow
|
||||
import { createStructuredSelector } from 'reselect'
|
||||
import { userAccountSelector } from '~/logic/wallets/store/selectors'
|
||||
|
||||
export type SelectorProps = {
|
||||
userAddress: typeof userAccountSelector,
|
||||
}
|
||||
|
||||
export default createStructuredSelector<Object, *>({
|
||||
userAddress: userAccountSelector,
|
||||
})
|
|
@ -1,34 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Bold from '~/components/layout/Bold'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import { THRESHOLD_PARAM } from '~/routes/safe/components/Threshold/ThresholdForm'
|
||||
|
||||
type FormProps = {
|
||||
values: Object,
|
||||
submitting: boolean,
|
||||
}
|
||||
|
||||
const spinnerStyle = {
|
||||
minHeight: '50px',
|
||||
}
|
||||
|
||||
const Review = () => (controls: React.Node, { values, submitting }: FormProps) => (
|
||||
<OpenPaper controls={controls}>
|
||||
<Heading tag="h2">Review the Threshold operation</Heading>
|
||||
<Paragraph align="left">
|
||||
<Bold>The new threshold will be: </Bold>
|
||||
{' '}
|
||||
{values[THRESHOLD_PARAM]}
|
||||
</Paragraph>
|
||||
<Block style={spinnerStyle}>
|
||||
{ submitting && <CircularProgress size={50} /> }
|
||||
</Block>
|
||||
</OpenPaper>
|
||||
)
|
||||
|
||||
export default Review
|
|
@ -1,46 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import Field from '~/components/forms/Field'
|
||||
import TextField from '~/components/forms/TextField'
|
||||
import {
|
||||
composeValidators, minValue, maxValue, mustBeInteger, required,
|
||||
} from '~/components/forms/validator'
|
||||
import { type Safe } from '~/routes/safe/store/models/safe'
|
||||
|
||||
export const THRESHOLD_PARAM = 'threshold'
|
||||
|
||||
type ThresholdProps = {
|
||||
numOwners: number,
|
||||
safe: Safe,
|
||||
}
|
||||
|
||||
const ThresholdForm = ({ numOwners, safe }: ThresholdProps) => (controls: React.Node) => (
|
||||
<OpenPaper controls={controls}>
|
||||
<Heading tag="h2" margin="lg">
|
||||
{'Change safe\'s threshold'}
|
||||
</Heading>
|
||||
<Heading tag="h4" margin="lg">
|
||||
{`Safe's owners: ${numOwners} and Safe's threshold: ${safe.get('threshold')}`}
|
||||
</Heading>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={THRESHOLD_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={composeValidators(
|
||||
required,
|
||||
mustBeInteger,
|
||||
minValue(1),
|
||||
maxValue(numOwners),
|
||||
)}
|
||||
placeholder="New threshold"
|
||||
text="Safe's threshold"
|
||||
/>
|
||||
</Block>
|
||||
</OpenPaper>
|
||||
)
|
||||
|
||||
export default ThresholdForm
|
|
@ -1,10 +0,0 @@
|
|||
// @flow
|
||||
import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions'
|
||||
|
||||
export type Actions = {
|
||||
fetchTransactions: typeof fetchTransactions,
|
||||
}
|
||||
|
||||
export default {
|
||||
fetchTransactions,
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import { connect } from 'react-redux'
|
||||
import { createTransaction } from '~/logic/safe/safeFrontendOperations'
|
||||
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
||||
import { type Safe } from '~/routes/safe/store/models/safe'
|
||||
import ThresholdForm, { THRESHOLD_PARAM } from './ThresholdForm'
|
||||
import selector, { type SelectorProps } from './selector'
|
||||
import actions, { type Actions } from './actions'
|
||||
import Review from './Review'
|
||||
|
||||
type Props = SelectorProps & Actions & {
|
||||
numOwners: number,
|
||||
safe: Safe,
|
||||
onReset: () => void,
|
||||
}
|
||||
|
||||
const getSteps = () => [
|
||||
'Fill Change threshold Form', 'Review change threshold operation',
|
||||
]
|
||||
|
||||
type State = {
|
||||
done: boolean,
|
||||
}
|
||||
|
||||
export const CHANGE_THRESHOLD_RESET_BUTTON_TEXT = 'SEE TXs'
|
||||
|
||||
class Threshold extends React.PureComponent<Props, State> {
|
||||
state = {
|
||||
done: false,
|
||||
}
|
||||
|
||||
onThreshold = async (values: Object) => {
|
||||
try {
|
||||
const { safe, userAddress, fetchTransactions } = this.props // , fetchThreshold } = this.props
|
||||
const newThreshold = values[THRESHOLD_PARAM]
|
||||
const safeAddress = safe.get('address')
|
||||
const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
|
||||
const nonce = await gnosisSafe.nonce()
|
||||
const data = gnosisSafe.contract.changeThreshold(newThreshold).encodeABI()
|
||||
await createTransaction(safe, `Change Safe's threshold [${nonce}]`, safeAddress, '0', nonce, userAddress, data)
|
||||
await fetchTransactions(safeAddress)
|
||||
this.setState({ done: true })
|
||||
} catch (error) {
|
||||
this.setState({ done: false })
|
||||
// eslint-disable-next-line
|
||||
console.log('Error while changing threshold ' + error)
|
||||
}
|
||||
}
|
||||
|
||||
onReset = () => {
|
||||
const { onReset } = this.props
|
||||
this.setState({ done: false })
|
||||
|
||||
onReset()
|
||||
}
|
||||
|
||||
render() {
|
||||
const { numOwners, safe } = this.props
|
||||
const { done } = this.state
|
||||
const steps = getSteps()
|
||||
const finishedButton = <Stepper.FinishButton title={CHANGE_THRESHOLD_RESET_BUTTON_TEXT} />
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Stepper
|
||||
finishedTransaction={done}
|
||||
finishedButton={finishedButton}
|
||||
onSubmit={this.onThreshold}
|
||||
steps={steps}
|
||||
onReset={this.onReset}
|
||||
>
|
||||
<Stepper.Page numOwners={numOwners} safe={safe}>
|
||||
{ ThresholdForm }
|
||||
</Stepper.Page>
|
||||
<Stepper.Page>
|
||||
{ Review }
|
||||
</Stepper.Page>
|
||||
</Stepper>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(selector, actions)(Threshold)
|
|
@ -1,11 +0,0 @@
|
|||
// @flow
|
||||
import { createStructuredSelector } from 'reselect'
|
||||
import { userAccountSelector } from '~/logic/wallets/store/selectors'
|
||||
|
||||
export type SelectorProps = {
|
||||
userAddress: typeof userAccountSelector,
|
||||
}
|
||||
|
||||
export default createStructuredSelector<Object, *>({
|
||||
userAddress: userAccountSelector,
|
||||
})
|
Loading…
Reference in New Issue