1import React, { useCallback, useEffect, useMemo, useState } from 'react';
2import cn from 'classnames';
3import { Button, Grid } from 'indigo-react';
4import Window from 'components/L2/Window/Window';
5import HeaderPane from 'components/L2/Window/HeaderPane';
6import BodyPane from 'components/L2/Window/BodyPane';
8import * as azimuth from 'azimuth-js';
9import ob from 'urbit-ob';
10import { Box, Icon, Row, Text } from '@tlon/indigo-react';
12import { useNetwork } from 'store/network';
13import { usePointCache } from 'store/pointCache';
14import { usePointCursor } from 'store/pointCursor';
15import { useRollerStore } from 'store/rollerStore';
17import * as need from 'lib/need';
18import useEthereumTransaction from 'lib/useEthereumTransaction';
19import { GAS_LIMITS } from 'lib/constants';
20import { patp2dec } from 'lib/patp2dec';
21import { getSpawnCandidate } from 'lib/child';
22import { useLocalRouter } from 'lib/LocalRouter';
23import { validateChild } from 'lib/validators';
24import { convertToInt } from 'lib/convertToInt';
25import { isGalaxy } from 'lib/utils/point';
26import useRoller from 'lib/useRoller';
27import { L1TxnType } from 'lib/types/PendingL1Transaction';
29import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
30import View from 'components/View';
31import CopiableAddress from 'components/copiable/CopiableAddress';
32import L2BackHeader from 'components/L2/Headers/L2BackHeader';
33import LoadingOverlay from 'components/L2/LoadingOverlay';
34import { PointInput, AddressInput } from 'form/Inputs';
38 buildAddressValidator,
40} from 'form/validators';
41import BridgeForm from 'form/BridgeForm';
42import FormError from 'form/FormError';
44import { PatpBadge } from './PatpBadge';
45import { AddressButton } from '../../components/AddressButton';
46import './IssueChild.scss';
48export function useIssueChild() {
49 const { contracts }: any = useNetwork();
50 const { syncDates }: any = usePointCache();
52 const _contracts = need.contracts(contracts);
54 const [spawnedPoint, setSpawnedPoint] = useState<number | string | null>();
56 return useEthereumTransaction(
58 (spawnedPoint: string | number, owner: string) => {
59 setSpawnedPoint(spawnedPoint);
60 return azimuth.ecliptic.spawn(_contracts, spawnedPoint, owner);
64 useCallback(() => syncDates(spawnedPoint), [spawnedPoint, syncDates]),
69export default function IssueChild() {
70 const { pop }: any = useLocalRouter();
71 const { api, checkForUpdates, spawnPoint } = useRoller();
72 const { pointCursor }: any = usePointCursor();
73 const { point } = useRollerStore();
75 const _point = convertToInt(need.point(pointCursor), 10);
76 const [availablePoints, setAvailablePoints] = useState<Set<number> | null>(
79 const [candidates, setCandidates] = useState<string[]>([]);
80 const [pointToSpawn, setPointToSpawn] = useState<number | null>();
81 const [destinationAddress, setDestinationAddress] = useState<string | null>();
82 const [l2SpawnSent, setL2SpawnSent] = useState(false);
83 const [loading, setLoading] = useState(false);
85 const fetchAvailablePoints = useCallback(async () => {
86 const available = await api.getUnspawned(_point);
87 setAvailablePoints(new Set(available));
90 const shuffle = useCallback(() => {
91 const newCandidates: string[] = [];
93 // 20 tries to randomize
94 for (let i = 0; i < 20; i++) {
95 const candidate = ob.patp(getSpawnCandidate(_point));
96 if (!newCandidates.includes(candidate)) {
97 newCandidates.push(candidate);
99 if (newCandidates.length === 3) {
104 setCandidates(newCandidates);
107 const handleShuffleClick = useCallback(() => {
112 // load the availablePoints for validation
113 fetchAvailablePoints();
114 // set the candidates
116 }, [fetchAvailablePoints, shuffle]);
118 const spawnNewPoint = useCallback(async () => {
119 if (pointToSpawn && destinationAddress) {
121 await spawnPoint(pointToSpawn, destinationAddress);
122 checkForUpdates({ point: pointToSpawn });
123 setPointToSpawn(null);
124 setDestinationAddress(null);
125 setL2SpawnSent(true);
128 }, [pointToSpawn, destinationAddress, spawnPoint, checkForUpdates]);
141 if (completed && pointToSpawn) {
145 id: `${pointToSpawn}`,
147 type: L1TxnType.spawn,
149 time: new Date().getTime(),
153 }, [completed]); // eslint-disable-line react-hooks/exhaustive-deps
155 const validateFormAsync = useCallback(
157 const point = patp2dec(values.point);
158 const hasPoint = availablePoints && availablePoints.has(point);
161 return { point: 'This point cannot be spawned.' };
167 const validateForm = useCallback(
168 (values, errors) => {
169 if (hasErrors(errors)) {
173 return validateFormAsync(values);
178 const validate = useMemo(
182 point: buildPointValidator(4, [validateChild(ob.patp(_point))]),
183 owner: buildAddressValidator(),
187 [_point, validateForm]
190 const onValues = useCallback(
191 ({ valid, values }) => {
193 construct(patp2dec(values.point), values.owner);
199 setPointToSpawn(patp2dec(values.point));
201 setPointToSpawn(null);
204 setDestinationAddress(values.owner);
206 [construct, unconstruct]
210 pointToSpawn && availablePoints && availablePoints.has(pointToSpawn);
212 destinationAddress && !buildAddressValidator()(destinationAddress);
214 const pointType = point.isStar ? 'planet' : 'star';
216 const getButtonText = () => {
218 return `Please enter a valid ${pointType}`;
219 } else if (!validAddress) {
220 return `Please enter a valid destination address`;
222 return `Spawn ${ob.patp(pointToSpawn)}`;
230 className="issue-child"
231 header={<L2BackHeader hideBalance={point.isL2Spawn} back={pop} />}>
232 <Window className="id-issue-child">
235 <h5>Spawn {isGalaxy(_point) ? 'Star' : 'Planet'}</h5>
239 <Box className="inner-container">
244 {({ handleSubmit, values }: any) => (
246 {(completed || l2SpawnSent) && (
250 style={{ fontSize: 14 }}
251 className={cn('f5 wrap', {
254 <span className="mono">{values.point}</span> has been
255 spawned and can be claimed by{' '}
256 <CopiableAddress>{values.owner}</CopiableAddress>.
263 onClick={() => pop()}>
269 {!(completed || l2SpawnSent) && (
275 disabled={inputsLocked}
279 {isDefaultState && candidates.length > 0 && (
281 <Text className={'input-hint'}>Perhaps:</Text>
283 <Box className={'patp-badge-container'}>
284 <Box className="badges">
285 {candidates.map(p => (
286 <PatpBadge className={'patp-badge'} key={p}>
291 <Box className="shuffle">
295 onClick={handleShuffleClick}></Icon>
307 label="Ethereum Address"
308 disabled={inputsLocked}
312 <AddressButton inputName="owner">
317 <Grid.Item full as={FormError} />
326 disabled={!validPoint || !validAddress}
327 onClick={spawnNewPoint}>
333 as={InlineEthereumTransaction}
335 onReturn={() => pop()}
346 <LoadingOverlay loading={loading} />
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list