Changeset View
Changeset View
Standalone View
Standalone View
web/cashtab/src/components/Tokens/CreateTokenForm.js
import React, { useState } from 'react'; | import React, { useState } from 'react'; | ||||
import { AntdFormWrapper } from '@components/Common/EnhancedInputs'; | import { AntdFormWrapper } from '@components/Common/EnhancedInputs'; | ||||
import { TokenCollapse } from '@components/Common/StyledCollapse'; | import { TokenCollapse } from '@components/Common/StyledCollapse'; | ||||
import { currency } from '@components/Common/Ticker.js'; | import { currency } from '@components/Common/Ticker.js'; | ||||
import { WalletContext } from '@utils/context'; | import { WalletContext } from '@utils/context'; | ||||
import { | import { | ||||
isValidTokenName, | isValidTokenName, | ||||
isValidTokenTicker, | isValidTokenTicker, | ||||
isValidTokenDecimals, | isValidTokenDecimals, | ||||
isValidTokenInitialQty, | isValidTokenInitialQty, | ||||
isValidTokenDocumentUrl, | isValidTokenDocumentUrl, | ||||
} from '@utils/validation'; | } from '@utils/validation'; | ||||
import { PlusSquareOutlined } from '@ant-design/icons'; | import { PlusSquareOutlined } from '@ant-design/icons'; | ||||
import { SmartButton } from '@components/Common/PrimaryButton'; | import { SmartButton } from '@components/Common/PrimaryButton'; | ||||
import { Collapse, Form, Input, Modal, notification, Spin } from 'antd'; | import { Collapse, Form, Input, Modal, notification } from 'antd'; | ||||
const { Panel } = Collapse; | const { Panel } = Collapse; | ||||
import Paragraph from 'antd/lib/typography/Paragraph'; | import Paragraph from 'antd/lib/typography/Paragraph'; | ||||
import { TokenParamLabel } from '@components/Common/Atoms'; | import { TokenParamLabel } from '@components/Common/Atoms'; | ||||
import { CashLoadingIcon } from '@components/Common/CustomIcons'; | const CreateTokenForm = ({ | ||||
BCH, | |||||
const CreateTokenForm = ({ BCH, getRestUrl, createToken, disabled }) => { | getRestUrl, | ||||
createToken, | |||||
disabled, | |||||
passLoadingStatus, | |||||
}) => { | |||||
const { wallet } = React.useContext(WalletContext); | const { wallet } = React.useContext(WalletContext); | ||||
//const { getBCH, getRestUrl, createToken } = useBCH(); | |||||
// New Token Name | // New Token Name | ||||
const [newTokenName, setNewTokenName] = useState(''); | const [newTokenName, setNewTokenName] = useState(''); | ||||
const [newTokenNameIsValid, setNewTokenNameIsValid] = useState(null); | const [newTokenNameIsValid, setNewTokenNameIsValid] = useState(null); | ||||
const handleNewTokenNameInput = e => { | const handleNewTokenNameInput = e => { | ||||
const { value } = e.target; | const { value } = e.target; | ||||
// validation | // validation | ||||
setNewTokenNameIsValid(isValidTokenName(value)); | setNewTokenNameIsValid(isValidTokenName(value)); | ||||
setNewTokenName(value); | setNewTokenName(value); | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | let tokenGenesisDataIsValid = | ||||
newTokenTickerIsValid && | newTokenTickerIsValid && | ||||
newTokenDecimalsIsValid && | newTokenDecimalsIsValid && | ||||
newTokenInitialQtyIsValid && | newTokenInitialQtyIsValid && | ||||
newTokenDocumentUrlIsValid; | newTokenDocumentUrlIsValid; | ||||
// Modal settings | // Modal settings | ||||
const [showConfirmCreateToken, setShowConfirmCreateToken] = useState(false); | const [showConfirmCreateToken, setShowConfirmCreateToken] = useState(false); | ||||
// Token creation loading | |||||
const [genesisLoading, setGenesisLoading] = useState(false); | |||||
const createPreviewedToken = async () => { | const createPreviewedToken = async () => { | ||||
setGenesisLoading(true); | passLoadingStatus(true); | ||||
// If data is for some reason not valid here, bail out | // If data is for some reason not valid here, bail out | ||||
if (!tokenGenesisDataIsValid) { | if (!tokenGenesisDataIsValid) { | ||||
return; | return; | ||||
} | } | ||||
// data must be valid and user reviewed to get here | // data must be valid and user reviewed to get here | ||||
const configObj = { | const configObj = { | ||||
name: newTokenName, | name: newTokenName, | ||||
Show All 24 Lines | const createPreviewedToken = async () => { | ||||
Token created! Click or tap here for more details | Token created! Click or tap here for more details | ||||
</Paragraph> | </Paragraph> | ||||
</a> | </a> | ||||
), | ), | ||||
duration: 5, | duration: 5, | ||||
}); | }); | ||||
} catch (e) { | } catch (e) { | ||||
// Set loading to false here as well, as balance may not change depending on where error occured in try loop | // Set loading to false here as well, as balance may not change depending on where error occured in try loop | ||||
setGenesisLoading(false); | passLoadingStatus(false); | ||||
let message; | let message; | ||||
if (!e.error && !e.message) { | if (!e.error && !e.message) { | ||||
message = `Transaction failed: no response from ${getRestUrl()}.`; | message = `Transaction failed: no response from ${getRestUrl()}.`; | ||||
} else if ( | } else if ( | ||||
/Could not communicate with full node or other external service/.test( | /Could not communicate with full node or other external service/.test( | ||||
e.error, | e.error, | ||||
) | ) | ||||
Show All 14 Lines | const createPreviewedToken = async () => { | ||||
message: 'Error', | message: 'Error', | ||||
description: message, | description: message, | ||||
duration: 5, | duration: 5, | ||||
}); | }); | ||||
console.error(e); | console.error(e); | ||||
} | } | ||||
// Hide the modal | // Hide the modal | ||||
setShowConfirmCreateToken(false); | setShowConfirmCreateToken(false); | ||||
// Stop spinner | |||||
passLoadingStatus(false); | |||||
}; | }; | ||||
return ( | return ( | ||||
<> | <> | ||||
<Modal | <Modal | ||||
title={`Please review and confirm your token settings.`} | title={`Please review and confirm your token settings.`} | ||||
visible={showConfirmCreateToken} | visible={showConfirmCreateToken} | ||||
onOk={createPreviewedToken} | onOk={createPreviewedToken} | ||||
onCancel={() => setShowConfirmCreateToken(false)} | onCancel={() => setShowConfirmCreateToken(false)} | ||||
> | > | ||||
<TokenParamLabel>Name:</TokenParamLabel> {newTokenName} | <TokenParamLabel>Name:</TokenParamLabel> {newTokenName} | ||||
<br /> | <br /> | ||||
<TokenParamLabel>Ticker:</TokenParamLabel> {newTokenTicker} | <TokenParamLabel>Ticker:</TokenParamLabel> {newTokenTicker} | ||||
<br /> | <br /> | ||||
<TokenParamLabel>Decimals:</TokenParamLabel> {newTokenDecimals} | <TokenParamLabel>Decimals:</TokenParamLabel> {newTokenDecimals} | ||||
<br /> | <br /> | ||||
<TokenParamLabel>Supply:</TokenParamLabel> {newTokenInitialQty} | <TokenParamLabel>Supply:</TokenParamLabel> {newTokenInitialQty} | ||||
<br /> | <br /> | ||||
<TokenParamLabel>Document URL:</TokenParamLabel>{' '} | <TokenParamLabel>Document URL:</TokenParamLabel>{' '} | ||||
{newTokenDocumentUrl === '' | {newTokenDocumentUrl === '' | ||||
? 'https://cashtabapp.com/' | ? 'https://cashtabapp.com/' | ||||
: newTokenDocumentUrl} | : newTokenDocumentUrl} | ||||
<br /> | <br /> | ||||
</Modal> | </Modal> | ||||
<> | <> | ||||
<Spin spinning={genesisLoading} indicator={CashLoadingIcon}> | |||||
<TokenCollapse | <TokenCollapse | ||||
collapsible={disabled ? 'disabled' : true} | collapsible={disabled ? 'disabled' : true} | ||||
disabled={disabled} | disabled={disabled} | ||||
style={{ | style={{ | ||||
marginBottom: '24px', | marginBottom: '24px', | ||||
}} | }} | ||||
> | > | ||||
<Panel header="Create Token" key="1"> | <Panel header="Create Token" key="1"> | ||||
<AntdFormWrapper> | <AntdFormWrapper> | ||||
<Form | <Form | ||||
size="small" | size="small" | ||||
style={{ | style={{ | ||||
width: 'auto', | width: 'auto', | ||||
}} | }} | ||||
> | > | ||||
<Form.Item | <Form.Item | ||||
validateStatus={ | validateStatus={ | ||||
newTokenNameIsValid === null || | newTokenNameIsValid === null || | ||||
newTokenNameIsValid | newTokenNameIsValid | ||||
? '' | ? '' | ||||
: 'error' | : 'error' | ||||
} | } | ||||
help={ | help={ | ||||
newTokenNameIsValid === null || | newTokenNameIsValid === null || | ||||
newTokenNameIsValid | newTokenNameIsValid | ||||
? '' | ? '' | ||||
: 'Token name must be a string between 1 and 68 characters long' | : 'Token name must be a string between 1 and 68 characters long' | ||||
} | } | ||||
> | > | ||||
<Input | <Input | ||||
addonBefore="Name" | addonBefore="Name" | ||||
placeholder="Enter a name for your token" | placeholder="Enter a name for your token" | ||||
name="newTokenName" | name="newTokenName" | ||||
value={newTokenName} | value={newTokenName} | ||||
onChange={e => | onChange={e => | ||||
handleNewTokenNameInput(e) | handleNewTokenNameInput(e) | ||||
} | } | ||||
/> | /> | ||||
</Form.Item> | </Form.Item> | ||||
<Form.Item | <Form.Item | ||||
validateStatus={ | validateStatus={ | ||||
newTokenTickerIsValid === null || | newTokenTickerIsValid === null || | ||||
newTokenTickerIsValid | newTokenTickerIsValid | ||||
? '' | ? '' | ||||
: 'error' | : 'error' | ||||
} | } | ||||
help={ | help={ | ||||
newTokenTickerIsValid === null || | newTokenTickerIsValid === null || | ||||
newTokenTickerIsValid | newTokenTickerIsValid | ||||
? '' | ? '' | ||||
: 'Ticker must be a string between 1 and 12 characters long' | : 'Ticker must be a string between 1 and 12 characters long' | ||||
} | } | ||||
> | > | ||||
<Input | <Input | ||||
addonBefore="Ticker" | addonBefore="Ticker" | ||||
placeholder="Enter a ticker for your token" | placeholder="Enter a ticker for your token" | ||||
name="newTokenTicker" | name="newTokenTicker" | ||||
value={newTokenTicker} | value={newTokenTicker} | ||||
onChange={e => | onChange={e => | ||||
handleNewTokenTickerInput(e) | handleNewTokenTickerInput(e) | ||||
} | } | ||||
/> | /> | ||||
</Form.Item> | </Form.Item> | ||||
<Form.Item | <Form.Item | ||||
validateStatus={ | validateStatus={ | ||||
newTokenDecimalsIsValid === null || | newTokenDecimalsIsValid === null || | ||||
newTokenDecimalsIsValid | newTokenDecimalsIsValid | ||||
? '' | ? '' | ||||
: 'error' | : 'error' | ||||
} | } | ||||
help={ | help={ | ||||
newTokenDecimalsIsValid === null || | newTokenDecimalsIsValid === null || | ||||
newTokenDecimalsIsValid | newTokenDecimalsIsValid | ||||
? '' | ? '' | ||||
: 'Token decimals must be an integer between 0 and 9' | : 'Token decimals must be an integer between 0 and 9' | ||||
} | } | ||||
> | > | ||||
<Input | <Input | ||||
addonBefore="Decimals" | addonBefore="Decimals" | ||||
placeholder="Enter number of decimal places" | placeholder="Enter number of decimal places" | ||||
name="newTokenDecimals" | name="newTokenDecimals" | ||||
type="number" | type="number" | ||||
value={newTokenDecimals} | value={newTokenDecimals} | ||||
onChange={e => | onChange={e => | ||||
handleNewTokenDecimalsInput(e) | handleNewTokenDecimalsInput(e) | ||||
} | } | ||||
/> | /> | ||||
</Form.Item> | </Form.Item> | ||||
<Form.Item | <Form.Item | ||||
validateStatus={ | validateStatus={ | ||||
newTokenInitialQtyIsValid === | newTokenInitialQtyIsValid === null || | ||||
null || | |||||
newTokenInitialQtyIsValid | newTokenInitialQtyIsValid | ||||
? '' | ? '' | ||||
: 'error' | : 'error' | ||||
} | } | ||||
help={ | help={ | ||||
newTokenInitialQtyIsValid === | newTokenInitialQtyIsValid === null || | ||||
null || | |||||
newTokenInitialQtyIsValid | newTokenInitialQtyIsValid | ||||
? '' | ? '' | ||||
: 'Token supply must be greater than 0 and less than 100,000,000,000. Token supply decimal places cannot exceed token decimal places.' | : 'Token supply must be greater than 0 and less than 100,000,000,000. Token supply decimal places cannot exceed token decimal places.' | ||||
} | } | ||||
> | > | ||||
<Input | <Input | ||||
addonBefore="Supply" | addonBefore="Supply" | ||||
placeholder="Enter the fixed supply of your token" | placeholder="Enter the fixed supply of your token" | ||||
name="newTokenInitialQty" | name="newTokenInitialQty" | ||||
type="number" | type="number" | ||||
value={newTokenInitialQty} | value={newTokenInitialQty} | ||||
onChange={e => | onChange={e => | ||||
handleNewTokenInitialQtyInput(e) | handleNewTokenInitialQtyInput(e) | ||||
} | } | ||||
/> | /> | ||||
</Form.Item> | </Form.Item> | ||||
<Form.Item | <Form.Item | ||||
validateStatus={ | validateStatus={ | ||||
newTokenDocumentUrlIsValid === | newTokenDocumentUrlIsValid === null || | ||||
null || | |||||
newTokenDocumentUrlIsValid | newTokenDocumentUrlIsValid | ||||
? '' | ? '' | ||||
: 'error' | : 'error' | ||||
} | } | ||||
help={ | help={ | ||||
newTokenDocumentUrlIsValid === | newTokenDocumentUrlIsValid === null || | ||||
null || | |||||
newTokenDocumentUrlIsValid | newTokenDocumentUrlIsValid | ||||
? '' | ? '' | ||||
: 'Document URL cannot exceed 68 characters' | : 'Document URL cannot exceed 68 characters' | ||||
} | } | ||||
> | > | ||||
<Input | <Input | ||||
addonBefore="Document URL" | addonBefore="Document URL" | ||||
placeholder="Enter a website for your token" | placeholder="Enter a website for your token" | ||||
name="newTokenDocumentUrl" | name="newTokenDocumentUrl" | ||||
value={newTokenDocumentUrl} | value={newTokenDocumentUrl} | ||||
onChange={e => | onChange={e => | ||||
handleNewTokenDocumentUrlInput( | handleNewTokenDocumentUrlInput(e) | ||||
e, | |||||
) | |||||
} | } | ||||
/> | /> | ||||
</Form.Item> | </Form.Item> | ||||
</Form> | </Form> | ||||
</AntdFormWrapper> | </AntdFormWrapper> | ||||
<SmartButton | <SmartButton | ||||
onClick={() => setShowConfirmCreateToken(true)} | onClick={() => setShowConfirmCreateToken(true)} | ||||
disabled={!tokenGenesisDataIsValid} | disabled={!tokenGenesisDataIsValid} | ||||
> | > | ||||
<PlusSquareOutlined /> | <PlusSquareOutlined /> | ||||
Create Token | Create Token | ||||
</SmartButton> | </SmartButton> | ||||
</Panel> | </Panel> | ||||
</TokenCollapse> | </TokenCollapse> | ||||
</Spin> | |||||
</> | </> | ||||
</> | </> | ||||
); | ); | ||||
}; | }; | ||||
/* | |||||
passLoadingStatus must receive a default prop that is a function | |||||
in order to pass the rendering unit test in CreateTokenForm.test.js | |||||
status => {console.log(status)} is an arbitrary stub function | |||||
*/ | |||||
CreateTokenForm.defaultProps = { | |||||
passLoadingStatus: status => { | |||||
console.log(status); | |||||
}, | |||||
}; | |||||
export default CreateTokenForm; | export default CreateTokenForm; |