Changeset View
Changeset View
Standalone View
Standalone View
web/cashtab/src/components/Tokens/CreateChildNftForm.js
- This file was added.
import React, { useState } from 'react'; | |||||
import PropTypes from 'prop-types'; | |||||
import { AntdFormWrapper } from '@components/Common/EnhancedInputs'; | |||||
import { TokenCollapse } from '@components/Common/StyledCollapse'; | |||||
import { currency } from '@components/Common/Ticker.js'; | |||||
import { WalletContext } from '@utils/context'; | |||||
import { | |||||
isValidChildNFTName, | |||||
isValidChildNFTTicker, | |||||
isValidChildNFTGroupId, | |||||
isValidChildNFTDocumentUrl, | |||||
} from '@utils/validation'; | |||||
import { PlusSquareOutlined } from '@ant-design/icons'; | |||||
import { SmartButton } from '@components/Common/PrimaryButton'; | |||||
import { Collapse, Form, Input, Modal } from 'antd'; | |||||
const { Panel } = Collapse; | |||||
import { ChildNFTParamLabel } from '@components/Common/Atoms'; | |||||
import { | |||||
createTokenNotification, | |||||
errorNotification, | |||||
} from '@components/Common/Notifications'; | |||||
const CreateChildNftForm = ({ | |||||
BCH, | |||||
getRestUrl, | |||||
createToken, | |||||
disabled, | |||||
passLoadingStatus, | |||||
}) => { | |||||
const { wallet } = React.useContext(WalletContext); | |||||
// New child NFT Name | |||||
const [newChildNFTName, setNewChildNFTName] = useState(''); | |||||
const [newChildNFTNameIsValid, setNewChildNFTNameIsValid] = useState(null); | |||||
const handleNewChildNFTNameInput = e => { | |||||
const { value } = e.target; | |||||
// validation | |||||
setNewChildNFTNameIsValid(isValidChildNFTName(value)); | |||||
setNewChildNFTName(value); | |||||
}; | |||||
// New child NFT Ticker | |||||
const [newChildNFTTicker, setNewChildNFTTicker] = useState(''); | |||||
const [newChildNFTTickerIsValid, setNewChildNFTTickerIsValid] = | |||||
useState(null); | |||||
const handleNewChildNFTTickerInput = e => { | |||||
const { value } = e.target; | |||||
// validation | |||||
setNewChildNFTTickerIsValid(isValidChildNFTTicker(value)); | |||||
setNewChildNFTTicker(value); | |||||
}; | |||||
// New child NFT Batons | |||||
const [newChildNFTGroupId, setNewChildNFTGroupId] = useState(0); | |||||
const [newChildNFTGroupIdIsValid, setNewChildNFTGroupIdIsValid] = | |||||
useState(true); | |||||
const handleNewChildNFTGroupIdInput = e => { | |||||
const { value } = e.target; | |||||
// validation | |||||
setNewChildNFTGroupIdIsValid(isValidChildNFTGroupId(value)); | |||||
// Also validate the supply here if it has not yet been set | |||||
/* | |||||
if (newChildNFTInitialQtyIsValid !== null) { | |||||
setNewTokenInitialQtyIsValid( | |||||
isValidChildNFTInitialQty(value, newChildNFTGroupId), | |||||
); | |||||
} | |||||
*/ | |||||
setNewChildNFTGroupId(value); | |||||
}; | |||||
// New New child NFT document URL | |||||
const [newChildNFTDocumentUrl, setNewChildNFTDocumentUrl] = useState(''); | |||||
// Start with this as true, field is not required | |||||
const [newChildNFTDocumentUrlIsValid, setNewChildNFTDocumentUrlIsValid] = | |||||
useState(true); | |||||
const handleNewTokenDocumentUrlInput = e => { | |||||
const { value } = e.target; | |||||
// validation | |||||
setNewChildNFTDocumentUrlIsValid(isValidChildNFTDocumentUrl(value)); | |||||
setNewChildNFTDocumentUrl(value); | |||||
}; | |||||
// New New child NFT fixed supply | |||||
// Only allow creation of fixed supply tokens until Minting support is added | |||||
// New New child NFT document hash | |||||
// Do not include this; questionable value to casual users and requires significant complication | |||||
// Only enable CreateChildNftForm button if all form entries are valid | |||||
let childNFTDataIsValid = | |||||
newChildNFTNameIsValid && | |||||
newChildNFTTickerIsValid && | |||||
newChildNFTGroupIdIsValid && | |||||
newChildNFTDocumentUrlIsValid; | |||||
// Modal settings | |||||
const [showConfirmCreateChildNFT, setShowConfirmCreateChildNFT] = | |||||
useState(false); | |||||
const createPreviewedChildNFT = async () => { | |||||
passLoadingStatus(true); | |||||
// If data is for some reason not valid here, bail out | |||||
if (!childNFTDataIsValid) { | |||||
return; | |||||
} | |||||
// data must be valid and user reviewed to get here | |||||
const configObj = { | |||||
name: newChildNFTName, | |||||
ticker: newChildNFTTicker, | |||||
documentUrl: | |||||
newChildNFTDocumentUrl === '' | |||||
? 'https://cashtab.com/' | |||||
: newChildNFTDocumentUrl, | |||||
tokenId: newChildNFTGroupId, | |||||
initialQty: '1', | |||||
documentHash: '', | |||||
}; | |||||
// create New child NFT with data in state fields | |||||
try { | |||||
const link = await createToken( | |||||
BCH, | |||||
wallet, | |||||
currency.defaultFee, | |||||
configObj, | |||||
); | |||||
createTokenNotification(link); | |||||
} catch (e) { | |||||
// Set loading to false here as well, as balance may not change depending on where error occured in try loop | |||||
passLoadingStatus(false); | |||||
let message; | |||||
if (!e.error && !e.message) { | |||||
message = `Transaction failed: no response from ${getRestUrl()}.`; | |||||
} else if ( | |||||
/Could not communicate with full node or other external service/.test( | |||||
e.error, | |||||
) | |||||
) { | |||||
message = 'Could not communicate with API. Please try again.'; | |||||
} else if ( | |||||
e.error && | |||||
e.error.includes( | |||||
'too-long-mempool-chain, too many unconfirmed ancestors [limit: 50] (code 64)', | |||||
) | |||||
) { | |||||
message = `The ${currency.ticker} you are trying to send has too many unconfirmed ancestors to send (limit 50). Sending will be possible after a block confirmation. Try again in about 10 minutes.`; | |||||
} else { | |||||
message = e.message || e.error || JSON.stringify(e); | |||||
} | |||||
errorNotification(e, message, 'Creating eToken'); | |||||
} | |||||
// Hide the modal | |||||
setShowConfirmCreateChildNFT(false); | |||||
// Stop spinner | |||||
passLoadingStatus(false); | |||||
}; | |||||
return ( | |||||
<> | |||||
<Modal | |||||
title={`Please review and confirm your NFT settings.`} | |||||
visible={showConfirmCreateChildNFT} | |||||
onOk={createPreviewedChildNFT} | |||||
onCancel={() => setShowConfirmCreateChildNFT(false)} | |||||
> | |||||
<ChildNFTParamLabel>Group NFT ID:</ChildNFTParamLabel>{' '} | |||||
{newChildNFTGroupId} | |||||
<br /> | |||||
<ChildNFTParamLabel>Name:</ChildNFTParamLabel> {newChildNFTName} | |||||
<br /> | |||||
<ChildNFTParamLabel>Ticker:</ChildNFTParamLabel>{' '} | |||||
{newChildNFTTicker} | |||||
<br /> | |||||
<ChildNFTParamLabel>Document URL:</ChildNFTParamLabel>{' '} | |||||
{newChildNFTDocumentUrl === '' | |||||
? 'https://cashtab.com/' | |||||
: newChildNFTDocumentUrl} | |||||
<br /> | |||||
</Modal> | |||||
<> | |||||
<TokenCollapse | |||||
collapsible={disabled ? 'disabled' : true} | |||||
disabled={disabled} | |||||
style={{ | |||||
marginBottom: '24px', | |||||
}} | |||||
> | |||||
<Panel header="Create Child NFT" key="1"> | |||||
<AntdFormWrapper> | |||||
<Form | |||||
size="small" | |||||
style={{ | |||||
width: 'auto', | |||||
}} | |||||
> | |||||
<Form.Item | |||||
validateStatus={ | |||||
newChildNFTGroupIdIsValid === null || | |||||
newChildNFTGroupIdIsValid | |||||
? '' | |||||
: 'error' | |||||
} | |||||
help={ | |||||
newChildNFTGroupIdIsValid === null || | |||||
newChildNFTGroupIdIsValid | |||||
? '' | |||||
: 'Group ID must not be blank' | |||||
} | |||||
> | |||||
<Input | |||||
addonBefore="Group NFT ID" | |||||
placeholder="Enter Group NFT ID" | |||||
name="newChildNFTGroupId" | |||||
value={newChildNFTGroupId} | |||||
onChange={e => | |||||
handleNewChildNFTGroupIdInput(e) | |||||
} | |||||
/> | |||||
</Form.Item> | |||||
<Form.Item | |||||
validateStatus={ | |||||
newChildNFTNameIsValid === null || | |||||
newChildNFTNameIsValid | |||||
? '' | |||||
: 'error' | |||||
} | |||||
help={ | |||||
newChildNFTNameIsValid === null || | |||||
newChildNFTNameIsValid | |||||
? '' | |||||
: 'Child NFT name must be a string between 1 and 68 characters long' | |||||
} | |||||
> | |||||
<Input | |||||
addonBefore="Name" | |||||
placeholder="Enter a name for your Child NFT" | |||||
name="newChildNFTName" | |||||
value={newChildNFTName} | |||||
onChange={e => | |||||
handleNewChildNFTNameInput(e) | |||||
} | |||||
/> | |||||
</Form.Item> | |||||
<Form.Item | |||||
validateStatus={ | |||||
newChildNFTTickerIsValid === null || | |||||
newChildNFTTickerIsValid | |||||
? '' | |||||
: 'error' | |||||
} | |||||
help={ | |||||
newChildNFTTickerIsValid === null || | |||||
newChildNFTTickerIsValid | |||||
? '' | |||||
: 'Ticker must be a string between 1 and 12 characters long' | |||||
} | |||||
> | |||||
<Input | |||||
addonBefore="Ticker" | |||||
placeholder="Enter a ticker for your Child NFT" | |||||
name="newChildNFTTicker" | |||||
value={newChildNFTTicker} | |||||
onChange={e => | |||||
handleNewChildNFTTickerInput(e) | |||||
} | |||||
/> | |||||
</Form.Item> | |||||
<Form.Item | |||||
validateStatus={ | |||||
newChildNFTDocumentUrlIsValid === | |||||
null || | |||||
newChildNFTDocumentUrlIsValid | |||||
? '' | |||||
: 'error' | |||||
} | |||||
help={ | |||||
newChildNFTDocumentUrlIsValid === | |||||
null || | |||||
newChildNFTDocumentUrlIsValid | |||||
? '' | |||||
: 'Document URL cannot exceed 68 characters' | |||||
} | |||||
> | |||||
<Input | |||||
addonBefore="Document URL" | |||||
placeholder="Enter a website for your Child NFT" | |||||
name="newChildNFTDocumentUrl" | |||||
value={newChildNFTDocumentUrl} | |||||
onChange={e => | |||||
handleNewTokenDocumentUrlInput(e) | |||||
} | |||||
/> | |||||
</Form.Item> | |||||
</Form> | |||||
</AntdFormWrapper> | |||||
<SmartButton | |||||
onClick={() => setShowConfirmCreateChildNFT(true)} | |||||
disabled={!childNFTDataIsValid} | |||||
> | |||||
<PlusSquareOutlined /> | |||||
Create Child NFT | |||||
</SmartButton> | |||||
</Panel> | |||||
</TokenCollapse> | |||||
</> | |||||
</> | |||||
); | |||||
}; | |||||
/* | |||||
passLoadingStatus must receive a default prop that is a function | |||||
in order to pass the rendering unit test in CreateChildNftForm.test.js | |||||
status => {console.log(status)} is an arbitrary stub function | |||||
*/ | |||||
CreateChildNftForm.defaultProps = { | |||||
passLoadingStatus: status => { | |||||
console.log(status); | |||||
}, | |||||
}; | |||||
CreateChildNftForm.propTypes = { | |||||
BCH: PropTypes.object, | |||||
getRestUrl: PropTypes.func, | |||||
createChildNFTToken: PropTypes.func, | |||||
disabled: PropTypes.bool, | |||||
passLoadingStatus: PropTypes.func, | |||||
}; | |||||
export default CreateChildNftForm; |