import { Just, Nothing } from 'folktale/maybe'; import React from 'react'; import { azimuth, ecliptic } from 'azimuth-js'; import * as ob from 'urbit-ob'; import * as need from '../lib/need'; import { H1, P, Anchor, ShowBlockie, ValidatedSigil, PointInput, AddressInput, InnerLabel, } from '../components/old/Base'; import StatelessTransaction from '../components/old/StatelessTransaction'; import { NETWORK_TYPES } from '../lib/network'; import { getSpawnCandidate } from '../lib/child'; import { canDecodePatp } from '../lib/txn'; import { isValidAddress } from '../lib/wallet'; import { withNetwork } from '../store/network'; import { compose } from '../lib/lib'; import { withPointCursor } from '../store/pointCursor'; import View from 'components/View'; const setFind = (set, pred) => { for (const e of set) { if (pred(e)) { return e; } } return undefined; }; class IssueChild extends React.Component { constructor(props) { super(props); const issuingPoint = need.point(props.pointCursor); const getCandidate = () => ob.patp(getSpawnCandidate(issuingPoint)); const suggestions = [ getCandidate(), getCandidate(), getCandidate(), getCandidate(), ]; this.state = { receivingAddress: '', issuingPoint: issuingPoint, desiredPoint: '', isAvailable: Nothing(), // use Nothing to allow attempt when offline suggestions: suggestions, validChildren: new Set(), autoComplete: '', }; this.handlePointInput = this.handlePointInput.bind(this); this.handleAddressInput = this.handleAddressInput.bind(this); this.createUnsignedTxn = this.createUnsignedTxn.bind(this); this.statelessRef = React.createRef(); } handleAddressInput = receivingAddress => { this.setState({ receivingAddress }); this.statelessRef.current.clearTxn(); }; componentWillMount() { const { issuingPoint } = this.state; const validContracts = need.contracts(this.props.contracts); azimuth .getUnspawnedChildren(validContracts, issuingPoint) .then(ps => this.setState({ validChildren: new Set(ps.map(ob.patp)) })); } handlePointInput(desiredPoint) { if (desiredPoint.length < 15) { const suggestedPoint = setFind(this.state.validChildren, e => e.startsWith(desiredPoint)) || ''; const rendered = ' '.repeat(desiredPoint.length) + suggestedPoint.substring(desiredPoint.length); const available = Just(this.state.validChildren.has(desiredPoint)); this.setState({ desiredPoint, isAvailable: available, autoComplete: rendered, }); this.statelessRef.current.clearTxn(); } } createUnsignedTxn() { const { state, props } = this; if (isValidAddress(state.receivingAddress) === false) return Nothing(); if (state.isAvailable === false) return Nothing(); if (canDecodePatp(state.desiredPoint) === false) return Nothing(); const validContracts = need.contracts(props.contracts); const pointDec = ob.patp2dec(state.desiredPoint); const txn = ecliptic.spawn( validContracts, pointDec, state.receivingAddress ); return Just(txn); } validatePoint = patp => { const point = this.point; let vpatp = false; try { vpatp = ob.isValidPatp(patp); } catch (_) {} let vchild = false; try { vchild = ob.sein(patp) === ob.patp(point); } catch (_) {} return vpatp && vchild; }; render() { const { props, state } = this; const validAddress = isValidAddress(state.receivingAddress); const canGenerate = props.web3.matchWith({ Nothing: () => { return validAddress === true; }, Just: () => { return validAddress === true && state.isAvailable.value === true; }, }); const esvisible = props.networkType === NETWORK_TYPES.ROPSTEN || props.networkType === NETWORK_TYPES.MAINNET; const esdomain = props.networkType === NETWORK_TYPES.ROPSTEN ? 'ropsten.etherscan.io' : 'etherscan.io'; return (

{'Issue a Child From '}{' '} {`${ob.patp(state.issuingPoint)}`}

{`Please enter the point you would like to issue, and specify the receiving Ethereum address. If you need to create an address, you can also use Wallet Generator.`}

{`Your point can only issue children with particular names. Some valid suggestions for `} {{ob.patp(state.issuingPoint)}} {' are '} {state.suggestions[0]} {', '} {state.suggestions[1]} {', and '} {state.suggestions[2]} {'.'}

{'Point to Issue'}
{state.autoComplete}
this.validatePoint(state.desiredPoint)} />
this.handleAddressInput(v)}> {'Receiving Address'} {'View on Etherscan ↗'}
); } } export default compose( withNetwork, withPointCursor )(IssueChild);