on 5a2bc24 help sign in
bridge/src/views/Point/Point.js
394 lines 11 kB history raw
1import React, { useCallback, useEffect, useState } from 'react';
2import cn from 'classnames';
3import { azimuth } from 'azimuth-js';
4import { Just } from 'folktale/maybe';
5import { Grid, Flex } from 'indigo-react';
6import { Box, Icon, Row, Button } from '@tlon/indigo-react';
8import { usePointCursor } from 'store/pointCursor';
9import { useWallet } from 'store/wallet';
10import { useRollerStore } from 'store/roller';
12import View from 'components/View';
13import Greeting from 'components/Greeting';
14import Passport from 'components/Passport';
15import Blinky from 'components/Blinky';
16import BarGraph from 'components/BarGraph';
17import Chip from 'components/Chip';
18import InviteSigilList from 'components/InviteSigilList';
19import { ForwardButton } from 'components/Buttons';
20import L2PointHeader from 'components/L2/Headers/L2PointHeader';
21import LayerIndicator from 'components/L2/LayerIndicator';
22import Card from 'components/L2/Card';
24import * as need from 'lib/need';
25import useInvites from 'lib/useInvites';
26import { useSyncExtras } from 'lib/useSyncPoints';
27import useCurrentPermissions from 'lib/useCurrentPermissions';
28import { useLocalRouter } from 'lib/LocalRouter';
29import Inviter from 'views/Invite/Inviter';
30import useRoller from 'lib/useRoller';
32import './Point.scss';
33import { isL2 } from 'lib/utils/roller';
34import { isDevelopment } from 'lib/flags';
35import { convertToInt } from 'lib/convertToInt';
36import { usePointCache } from 'store/pointCache';
37import Modal from 'components/L2/Modal';
38import { isPlanet } from 'lib/utils/point';
40function InviteForm({
41 showInviteForm,
42 setShowInviteForm,
43 acceptedInvites,
44 acceptedPoints,
45 availableInvites,
46 goCohort,
47 pendingPoints,
48 sentInvites,
49 showInvites,
50}) {
51 const _totalInvites =
52 sentInvites.getOrElse(0) + availableInvites.getOrElse(0);
53 const _pendingInvites = pendingPoints.getOrElse([]).length;
55 return (
56 <>
57 <Grid.Item cols={[1, 11]}>
58 Invite Group
59 <br />
60 </Grid.Item>
62 <Grid.Item
63 className={cn('t-right underline pointer-hover', {
64 gray4: sentInvites.getOrElse(0) === 0,
65 })}
66 onClick={goCohort}
67 cols={[11, 13]}>
68 View
69 </Grid.Item>
70 <Grid.Item full>
71 <Flex align="center">
72 <Flex.Item>
73 {acceptedInvites.getOrElse(0)} / {_totalInvites}
74 </Flex.Item>
75 {_pendingInvites > 0 && (
76 <Flex.Item as={Chip} className="bg-yellow1 yellow4">
77 {_pendingInvites} pending
78 </Flex.Item>
79 )}
80 </Flex>
81 </Grid.Item>
83 {showInvites && (
84 <>
85 <Grid.Item
86 full
87 as={BarGraph}
88 available={availableInvites}
89 sent={sentInvites}
90 accepted={acceptedInvites}
91 />
92 <Grid.Item
93 full
94 as={InviteSigilList}
95 pendingPoints={pendingPoints}
96 acceptedPoints={acceptedPoints}
97 />
98 </>
99 )}
100 {!showInvites && (
101 <>
102 <Grid.Item full className="b-gray4 b-dotted b1 self-center">
103 <div className="p4 pv8 t-center gray4">
104 Start your invite group by adding members
105 </div>
106 </Grid.Item>
107 </>
108 )}
109 {!showInviteForm && availableInvites.getOrElse(0) > 0 && (
110 <Grid.Item
111 full
112 solid
113 as={Button}
114 center
115 onClick={() => setShowInviteForm(true)}>
116 Add Members
117 </Grid.Item>
118 )}
119 {showInviteForm && <Inviter />}
120 <Grid.Item full className="mb2" />
121 </>
122 );
125export default function Point() {
126 const { pop, push, names } = useLocalRouter();
127 const { wallet, authToken } = useWallet();
128 const { pointCursor } = usePointCursor();
129 const point = need.point(pointCursor);
130 const { getDetails } = usePointCache();
131 // sync the current cursor
132 useSyncExtras([point]);
134 const { api, getInvites } = useRoller();
135 const {
136 pendingTransactions,
137 nextRoll,
138 invites,
139 setCurrentPoint,
140 setPendingTransactions,
141 currentL2,
142 setNonces,
143 increaseNonce,
144 nonces,
145 } = useRollerStore();
147 // const pointSize = azimuth.getPointSize(point);
148 // const details = need.details(getDetails(point));
149 // const details = getDetails(point);
150 // console.log(details);
151 // const isStarOrGalaxy = pointSize !== azimuth.PointSize.Planet;
152 // const networkRevision =
153 // Just.hasInstance(details) &&
154 // convertToInt(need.details(details).keyRevisionNumber, 10);
155 // console.log(networkRevision);
156 const networkKeysNotSet = false;
157 // networkRevision && !currentL2 && isStarOrGalaxy && networkRevision === 0;
159 const [showModal, setShowModal] = useState(networkKeysNotSet);
161 // useEffect(() => {
162 // if (networkKeysNotSet) {
163 // setShowModal(true);
164 // }
165 // }, [networkKeysNotSet]);
167 useEffect(
168 () => {
169 const loadL2Info = async () => {
170 const getTransactions = async () => {
171 const pointInfo = await api.getPoint(Number(point));
172 const pendingTxs = await api.getPendingByShip(Number(point));
173 if (isDevelopment) {
174 console.log('POINT INFO', pointInfo);
175 }
176 setNonces(point, pointInfo.ownership);
178 for (let index = 0; index < pendingTxs.length; index++) {
179 const proxy = pendingTxs[index].rawTx?.from?.proxy;
180 console.log(pointInfo.ownership, proxy);
181 increaseNonce(point, proxy);
182 }
183 setCurrentPoint(pointInfo);
184 getInvites(isL2(pointInfo.dominion));
185 };
187 getTransactions();
188 };
190 loadL2Info();
191 },
192 [
193 // TODO: adding this causes the effect to be called constantly...
194 // api,
195 // point,
196 // authToken,
197 // setPendingTransactions,
198 // nonces,
199 // setCurrentPoint,
200 // increaseNonce,
201 // getInvites,
202 // setNonces,
203 ]
204 );
206 const { isParent, canManage, canSpawn, canVote } = useCurrentPermissions();
208 // L1 invites
209 const l1Invites = useInvites(point);
210 const {
211 availableInvites,
212 sentInvites,
213 acceptedInvites,
214 pendingPoints,
215 acceptedPoints,
216 } = l1Invites;
218 const showInvites = !(
219 acceptedInvites.getOrElse(0) === 0 && sentInvites.getOrElse(0) === 0
220 );
222 const hasInvites = showInvites || availableInvites.getOrElse(0) !== 0;
224 const loadedInvites = Just.hasInstance(availableInvites);
225 //
226 // availableInvites.getOrElse(0) === 0
228 const goSenate = useCallback(() => push(names.SENATE), [push, names]);
230 const goCohort = useCallback(() => push(names.INVITE_COHORT), [push, names]);
232 const goUrbitOS = useCallback(() => push(names.URBIT_OS), [push, names]);
234 const goUrbitID = useCallback(() => push(names.URBIT_ID), [push, names]);
236 const goResidents = useCallback(() => push(names.RESIDENTS), [push, names]);
238 const goIssuePoint = useCallback(() => push(names.ISSUE_CHILD), [
239 names.ISSUE_CHILD,
240 push,
241 ]);
243 const planet = isPlanet(point);
245 const [showInviteForm, setShowInviteForm] = useState(false);
247 const senateButton = (() => {
248 if (azimuth.getPointSize(point) !== azimuth.PointSize.Galaxy) {
249 return null;
250 }
251 return (
252 <>
253 <Grid.Item
254 full
255 as={ForwardButton}
256 disabled={!canVote}
257 onClick={goSenate}>
258 Senate
259 </Grid.Item>
260 <Grid.Divider />
261 </>
262 );
263 })();
265 // sync the current cursor
266 useSyncExtras([point]);
268 const address = need.addressFromWallet(wallet);
269 const numPending = pendingTransactions.filter(
270 ({ rawTx }) => rawTx?.tx?.type === 'spawn'
271 ).length;
273 return (
274 <View
275 pop={pop}
276 inset
277 className="point"
278 hideBack
279 header={
280 <L2PointHeader
281 hideTimer={!!numPending}
282 numInvites={invites.length}
283 hideInvites={networkKeysNotSet}
284 />
285 }>
286 <Greeting point={point} />
287 {!!numPending && (
288 <div className="transaction">
289 <Row className="title-row">
290 <div className="title">
291 {numPending} Planet
292 {numPending > 1 ? 's' : ''} Spawned
293 </div>
294 <div className="rollup-timer">
295 <Icon icon="Clock" />
296 {nextRoll}
297 </div>
298 </Row>
299 <Row className="info-row">
300 <LayerIndicator layer={2} size="sm" />
301 <div className="date"></div>
302 </Row>
303 </div>
304 )}
305 <Passport
306 point={Just(point)}
307 address={Just(address)}
308 animationMode={'slide'}
309 />
310 <Grid gap={4}>
311 {isParent && (
312 <Grid.Item full as={Flex} justify="between">
313 <Card
314 title="Residency"
315 subtitle="Manage peers that you service"
316 onClick={goResidents}
317 />
318 </Grid.Item>
319 )}
320 {planet && hasInvites && (
321 <InviteForm
322 showInviteForm={showInviteForm}
323 setShowInviteForm={setShowInviteForm}
324 acceptedInvites={acceptedInvites}
325 acceptedPoints={acceptedPoints}
326 availableInvites={availableInvites}
327 goCohort={goCohort}
328 pendingPoints={pendingPoints}
329 sentInvites={sentInvites}
330 showInvites={showInvites}
331 />
332 )}
333 {!loadedInvites && planet && (
334 <Grid.Item className="mv2" full>
335 Invite Group <Blinky />
336 </Grid.Item>
337 )}
338 {/* {inviteButton} */}
339 <Grid.Item full as={Flex} justify="between">
340 <Card
341 title="ID"
342 subtitle="Identity and security settings"
343 icon={<Icon icon="User" />}
344 onClick={goUrbitID}
345 disabled={!canManage}
346 />
347 </Grid.Item>
348 <Grid.Item full as={Flex} justify="between">
349 <Card
350 title="OS"
351 subtitle="Urbit OS Settings"
352 icon={<Icon icon="Server" />}
353 onClick={goUrbitOS}
354 disabled={!canManage}
355 />
356 </Grid.Item>
357 {isParent && (
358 <>
359 <Grid.Item
360 full
361 as={ForwardButton}
362 disabled={!canSpawn}
363 onClick={goIssuePoint}>
364 Issue Point
365 </Grid.Item>
366 <Grid.Divider />
367 </>
368 )}
369 {senateButton}
370 </Grid>
371 <Modal show={showModal} hide={() => setShowModal(false)}>
372 <Box className="network-keys-modal">
373 <Box className="close" onClick={() => setShowModal(false)}>
374 &#215;
375 </Box>
376 <Box className="title">Network Keys Not Set</Box>
377 <Box className="message">
378 This point's network keys are not set. The network keys must be set
379 to spawn points or migrate to Layer 2. Please set them in Urbit OS
380 Settings.
381 </Box>
382 <Row className="buttons">
383 <Button className="cancel" onClick={() => setShowModal(false)}>
384 Cancel
385 </Button>
386 <Button className="migrate" onClick={goUrbitOS}>
387 Set Keys
388 </Button>
389 </Row>
390 </Box>
391 </Modal>
392 </View>
393 );