on cbb04ce help sign in
bridge/src/views/IssueChild.js
173 lines 4.6 kB history raw
1import React, { useCallback, useEffect, useMemo, useState } from 'react';
2import { Nothing, Just } from 'folktale/maybe';
3import cn from 'classnames';
4import { Grid, Text, Input } from 'indigo-react';
5import * as azimuth from 'azimuth-js';
6import * as ob from 'urbit-ob';
8import { useNetwork } from 'store/network';
9import { usePointCache } from 'store/pointCache';
10import { usePointCursor } from 'store/pointCursor';
12import * as need from 'lib/need';
13import { useAddressInput, usePointInput } from 'lib/useInputs';
14import useEthereumTransaction from 'lib/useEthereumTransaction';
15import { GAS_LIMITS } from 'lib/constants';
16import patp2dec from 'lib/patp2dec';
17import useLifecycle from 'lib/useLifecycle';
18import { validateNameInNumberSet } from 'lib/validators';
19import { getSpawnCandidate } from 'lib/child';
20import { useLocalRouter } from 'lib/LocalRouter';
22import ViewHeader from 'components/ViewHeader';
23import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
24import View from 'components/View';
26function useIssueChild() {
27 const { contracts } = useNetwork();
28 const { syncKnownPoint } = usePointCache();
30 const _contracts = need.contracts(contracts);
32 const [spawnedPoint, setSpawnedPoint] = useState();
34 return useEthereumTransaction(
35 useCallback(
36 (spawnedPoint, owner) => {
37 setSpawnedPoint(spawnedPoint);
38 return azimuth.ecliptic.spawn(_contracts, spawnedPoint, owner);
39 },
40 [_contracts]
41 ),
42 useCallback(() => syncKnownPoint(spawnedPoint), [
43 spawnedPoint,
44 syncKnownPoint,
45 ]),
46 GAS_LIMITS.DEFAULT
47 );
50export default function IssueChild() {
51 const { pop } = useLocalRouter();
52 const { contracts } = useNetwork();
53 const { pointCursor } = usePointCursor();
55 const _contracts = need.contracts(contracts);
56 const _point = parseInt(need.point(pointCursor), 10);
58 const [availablePoints, setAvailablePoints] = useState(Nothing());
60 const candidates = useMemo(() => {
61 const getCandidate = () => ob.patp(getSpawnCandidate(_point));
63 return [getCandidate(), getCandidate(), getCandidate(), getCandidate()];
64 }, [_point]);
66 const {
67 isDefaultState,
68 construct,
69 unconstruct,
70 completed,
71 inputsLocked,
72 bind,
73 } = useIssueChild();
75 const validators = useMemo(
76 () => [
77 validateNameInNumberSet(
78 availablePoints.getOrElse(new Set()),
79 'This point cannot be spawned.'
80 ),
81 ],
82 [availablePoints]
83 );
84 const [
85 pointNameInput,
86 { pass: validPointName, value: pointName },
87 // ^ we use value: here so our effect runs onChange
88 ] = usePointInput({
89 name: 'point',
90 disabled: inputsLocked,
91 autoFocus: true,
92 validators,
93 error: availablePoints.matchWith({
94 Nothing: () => 'Loading availability...',
95 Just: () => undefined,
96 }),
97 });
99 const [ownerInput, { pass: validOwner, data: owner }] = useAddressInput({
100 name: 'owner',
101 label: `Ethereum Address`,
102 disabled: inputsLocked,
103 });
105 useEffect(() => {
106 if (validPointName && validOwner) {
107 construct(patp2dec(pointName), owner);
108 } else {
109 unconstruct();
110 }
111 }, [owner, construct, unconstruct, validPointName, validOwner, pointName]);
113 useLifecycle(() => {
114 let mounted = true;
116 (async () => {
117 const availablePoints = await azimuth.azimuth.getUnspawnedChildren(
118 _contracts,
119 _point
120 );
122 if (!mounted) {
123 return;
124 }
126 setAvailablePoints(Just(new Set(availablePoints)));
127 })();
129 return () => (mounted = false);
130 });
132 return (
133 <View pop={pop} inset>
134 <Grid>
135 <Grid.Item full as={ViewHeader}>
136 Issue Child Point
137 </Grid.Item>
139 {isDefaultState && (
140 <Grid.Item full as={Text}>
141 Perhaps one of {candidates.slice(0, 3).join(', ')}, or{' '}
142 {candidates[candidates.length - 1]}?
143 </Grid.Item>
144 )}
146 {completed && (
147 <Grid.Item
148 full
149 as={Text}
150 className={cn('f5', {
151 green3: completed,
152 })}>
153 {pointName} has been spawned and can be claimed by {owner}.
154 </Grid.Item>
155 )}
157 {!completed && (
158 <>
159 <Grid.Item full as={Input} {...pointNameInput} className="mt4" />
160 <Grid.Item full as={Input} {...ownerInput} className="mb4" />
161 </>
162 )}
164 <Grid.Item
165 full
166 as={InlineEthereumTransaction}
167 {...bind}
168 onReturn={() => pop()}
169 />
170 </Grid>
171 </View>
172 );