on b2d092f help sign in
bridge/src/views/IssueChild.js
196 lines 5.3 kB history raw
1import React, { useCallback, useMemo, useState } from 'react';
2import cn from 'classnames';
3import { Grid, Text } from 'indigo-react';
4import * as azimuth from 'azimuth-js';
5import ob from 'urbit-ob';
7import { useNetwork } from 'store/network';
8import { usePointCache } from 'store/pointCache';
9import { usePointCursor } from 'store/pointCursor';
11import * as need from 'lib/need';
12import useEthereumTransaction from 'lib/useEthereumTransaction';
13import { GAS_LIMITS } from 'lib/constants';
14import patp2dec from 'lib/patp2dec';
15import { getSpawnCandidate } from 'lib/child';
16import { useLocalRouter } from 'lib/LocalRouter';
17import useConstant from 'lib/useConstant';
19import ViewHeader from 'components/ViewHeader';
20import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
21import View from 'components/View';
22import { PointInput, AddressInput } from 'form/Inputs';
23import {
24 composeValidator,
25 buildPointValidator,
26 buildAddressValidator,
27 hasErrors,
28} from 'form/validators';
29import BridgeForm from 'form/BridgeForm';
30import FormError from 'form/FormError';
31import CopiableAddress from 'components/CopiableAddress';
32import convertToInt from 'lib/convertToInt';
34function useIssueChild() {
35 const { contracts } = useNetwork();
36 const { syncKnownPoint } = usePointCache();
38 const _contracts = need.contracts(contracts);
40 const [spawnedPoint, setSpawnedPoint] = useState();
42 return useEthereumTransaction(
43 useCallback(
44 (spawnedPoint, owner) => {
45 setSpawnedPoint(spawnedPoint);
46 return azimuth.ecliptic.spawn(_contracts, spawnedPoint, owner);
47 },
48 [_contracts]
49 ),
50 useCallback(() => syncKnownPoint(spawnedPoint), [
51 spawnedPoint,
52 syncKnownPoint,
53 ]),
54 GAS_LIMITS.DEFAULT
55 );
58export default function IssueChild() {
59 const { pop } = useLocalRouter();
60 const { contracts } = useNetwork();
61 const { pointCursor } = usePointCursor();
63 const _contracts = need.contracts(contracts);
64 const _point = convertToInt(need.point(pointCursor), 10);
66 const availablePointsPromise = useConstant(() =>
67 azimuth.azimuth
68 .getUnspawnedChildren(_contracts, _point)
69 .then(points => new Set(points))
70 );
72 const candidates = useMemo(() => {
73 const getCandidate = () => ob.patp(getSpawnCandidate(_point));
75 return [getCandidate(), getCandidate(), getCandidate(), getCandidate()];
76 }, [_point]);
78 const {
79 isDefaultState,
80 construct,
81 unconstruct,
82 completed,
83 inputsLocked,
84 bind,
85 } = useIssueChild();
87 const validateFormAsync = useCallback(
88 async values => {
89 const point = patp2dec(values.point);
90 const hasPoint = (await availablePointsPromise).has(point);
92 if (!hasPoint) {
93 return { point: 'This point cannot be spawned.' };
94 }
95 },
96 [availablePointsPromise]
97 );
99 const validateForm = useCallback(
100 (values, errors) => {
101 if (hasErrors(errors)) {
102 return errors;
103 }
105 return validateFormAsync(values, errors);
106 },
107 [validateFormAsync]
108 );
110 const validate = useMemo(
111 () =>
112 composeValidator(
113 {
114 point: buildPointValidator(4),
115 owner: buildAddressValidator(),
116 },
117 validateForm
118 ),
119 [validateForm]
120 );
122 const onValues = useCallback(
123 ({ valid, values }) => {
124 if (valid) {
125 construct(patp2dec(values.point), values.owner);
126 } else {
127 unconstruct();
128 }
129 },
130 [construct, unconstruct]
131 );
133 return (
134 <View pop={pop} inset>
135 <Grid>
136 <Grid.Item full as={ViewHeader}>
137 Issue Child Point
138 </Grid.Item>
140 {isDefaultState && (
141 <Grid.Item full as={Text}>
142 Perhaps one of {candidates.slice(0, 3).join(', ')}, or{' '}
143 {candidates[candidates.length - 1]}?
144 </Grid.Item>
145 )}
147 <BridgeForm validate={validate} onValues={onValues}>
148 {({ handleSubmit, values }) => (
149 <>
150 {completed && (
151 <Grid.Item
152 full
153 as={Text}
154 className={cn('f5 wrap', {
155 green3: completed,
156 })}>
157 {values.point} has been spawned and can be claimed by{' '}
158 <CopiableAddress>{values.owner}</CopiableAddress>.
159 </Grid.Item>
160 )}
162 {!completed && (
163 <>
164 <Grid.Item
165 full
166 as={PointInput}
167 name="point"
168 disabled={inputsLocked}
169 className="mt4"
170 />
171 <Grid.Item
172 full
173 as={AddressInput}
174 className="mb4"
175 name="owner"
176 label="Ethereum Address"
177 disabled={inputsLocked}
178 />
179 </>
180 )}
182 <Grid.Item full as={FormError} />
184 <Grid.Item
185 full
186 as={InlineEthereumTransaction}
187 {...bind}
188 onReturn={() => pop()}
189 />
190 </>
191 )}
192 </BridgeForm>
193 </Grid>
194 </View>
195 );