on 1ac970d help sign in
bridge/src/views/Point.js
310 lines 8.6 kB history raw
1import React, { useCallback, useEffect, useState } from 'react';
2import cn from 'classnames';
3import { Just } from 'folktale/maybe';
4import { Grid, Flex, Button } from 'indigo-react';
5import { Icon, Row } from '@tlon/indigo-react';
6import { azimuth } from 'azimuth-js';
8import { usePointCursor } from 'store/pointCursor';
9import { useWallet } from 'store/wallet';
10import { isL2, 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';
34function InviteForm({
35 showInviteForm,
36 setShowInviteForm,
37 acceptedInvites,
38 acceptedPoints,
39 availableInvites,
40 goCohort,
41 pendingPoints,
42 sentInvites,
43 showInvites,
44}) {
45 const _totalInvites =
46 sentInvites.getOrElse(0) + availableInvites.getOrElse(0);
47 const _pendingInvites = pendingPoints.getOrElse([]).length;
49 return (
50 <>
51 <Grid.Item cols={[1, 11]}>
52 Invite Group
53 <br />
54 </Grid.Item>
56 <Grid.Item
57 className={cn('t-right underline pointer-hover', {
58 gray4: sentInvites.getOrElse(0) === 0,
59 })}
60 onClick={goCohort}
61 cols={[11, 13]}>
62 View
63 </Grid.Item>
64 <Grid.Item full>
65 <Flex align="center">
66 <Flex.Item>
67 {acceptedInvites.getOrElse(0)} / {_totalInvites}
68 </Flex.Item>
69 {_pendingInvites > 0 && (
70 <Flex.Item as={Chip} className="bg-yellow1 yellow4">
71 {_pendingInvites} pending
72 </Flex.Item>
73 )}
74 </Flex>
75 </Grid.Item>
77 {showInvites && (
78 <>
79 <Grid.Item
80 full
81 as={BarGraph}
82 available={availableInvites}
83 sent={sentInvites}
84 accepted={acceptedInvites}
85 />
86 <Grid.Item
87 full
88 as={InviteSigilList}
89 pendingPoints={pendingPoints}
90 acceptedPoints={acceptedPoints}
91 />
92 </>
93 )}
94 {!showInvites && (
95 <>
96 <Grid.Item full className="b-gray4 b-dotted b1 self-center">
97 <div className="p4 pv8 t-center gray4">
98 Start your invite group by adding members
99 </div>
100 </Grid.Item>
101 </>
102 )}
103 {!showInviteForm && availableInvites.getOrElse(0) > 0 && (
104 <Grid.Item
105 full
106 solid
107 as={Button}
108 center
109 onClick={() => setShowInviteForm(true)}>
110 Add Members
111 </Grid.Item>
112 )}
113 {showInviteForm && <Inviter />}
114 <Grid.Item full className="mb2" />
115 </>
116 );
119export default function Point() {
120 const { pop, push, names } = useLocalRouter();
121 const { wallet, authToken } = useWallet();
122 const { pointCursor } = usePointCursor();
123 const point = need.point(pointCursor);
125 const { api, getInvites } = useRoller();
126 const {
127 pendingTransactions,
128 nextRoll,
129 invites,
130 setCurrentPoint,
131 setPendingTransactions,
132 } = useRollerStore();
134 const [loaded, setLoaded] = useState(false);
136 useEffect(() => {
137 const loadL2Info = async () => {
138 const getTransactions = async () => {
139 const pointInfo = await api.getPoint(Number(point));
140 setCurrentPoint(pointInfo);
142 getInvites(isL2(pointInfo.dominion));
143 };
145 if (!loaded) {
146 getTransactions();
147 setLoaded(true);
148 }
149 };
151 loadL2Info();
152 }, [api, point, authToken, setPendingTransactions, loaded]); // eslint-disable-line
154 const { isParent, canManage, canSpawn, canVote } = useCurrentPermissions();
156 // L1 invites
157 const l1Invites = useInvites(point);
158 const {
159 availableInvites,
160 sentInvites,
161 acceptedInvites,
162 pendingPoints,
163 acceptedPoints,
164 } = l1Invites;
166 const showInvites = !(
167 acceptedInvites.getOrElse(0) === 0 && sentInvites.getOrElse(0) === 0
168 );
170 const hasInvites = showInvites || availableInvites.getOrElse(0) !== 0;
172 const loadedInvites = Just.hasInstance(availableInvites);
173 //
174 // availableInvites.getOrElse(0) === 0
176 const goSenate = useCallback(() => push(names.SENATE), [push, names]);
178 const goCohort = useCallback(() => push(names.INVITE_COHORT), [push, names]);
180 const goUrbitOS = useCallback(() => push(names.URBIT_OS), [push, names]);
182 const goUrbitID = useCallback(() => push(names.URBIT_ID), [push, names]);
184 const goResidents = useCallback(() => push(names.RESIDENTS), [push, names]);
186 const goIssuePoint = useCallback(() => push(names.ISSUE_CHILD), [
187 names.ISSUE_CHILD,
188 push,
189 ]);
191 const isPlanet = azimuth.getPointSize(point) === azimuth.PointSize.Planet;
193 const [showInviteForm, setShowInviteForm] = useState(false);
195 const senateButton = (() => {
196 if (azimuth.getPointSize(point) !== azimuth.PointSize.Galaxy) {
197 return null;
198 }
199 return (
200 <>
201 <Grid.Item
202 full
203 as={ForwardButton}
204 disabled={!canVote}
205 onClick={goSenate}>
206 Senate
207 </Grid.Item>
208 <Grid.Divider />
209 </>
210 );
211 })();
213 // sync the current cursor
214 useSyncExtras([point]);
216 const address = need.addressFromWallet(wallet);
217 const numPending = pendingTransactions.length;
219 return (
220 <View
221 pop={pop}
222 inset
223 className="point"
224 header={
225 <L2PointHeader hideTimer={!!numPending} numInvites={invites.length} />
226 }>
227 <Greeting point={point} />
228 {!!numPending && (
229 <div className="transaction">
230 <Row className="title-row">
231 <div className="title">
232 {numPending} Planet{numPending > 1 ? 's' : ''} Spawned
233 </div>
234 <div className="rollup-timer">{nextRoll}</div>
235 </Row>
236 <Row className="info-row">
237 <LayerIndicator layer={2} size="sm" />
238 <div className="date"></div>
239 </Row>
240 </div>
241 )}
242 <Passport
243 point={Just(point)}
244 address={Just(address)}
245 animationMode={'slide'}
246 />
247 <Grid gap={4}>
248 {isParent && (
249 <Grid.Item full as={Flex} justify="between">
250 <Card
251 title="Residency"
252 subtitle="Manage peers that you service"
253 onClick={goResidents}
254 />
255 </Grid.Item>
256 )}
257 {isPlanet && hasInvites && (
258 <InviteForm
259 showInviteForm={showInviteForm}
260 setShowInviteForm={setShowInviteForm}
261 acceptedInvites={acceptedInvites}
262 acceptedPoints={acceptedPoints}
263 availableInvites={availableInvites}
264 goCohort={goCohort}
265 pendingPoints={pendingPoints}
266 sentInvites={sentInvites}
267 showInvites={showInvites}
268 />
269 )}
270 {!loadedInvites && isPlanet && (
271 <Grid.Item className="mv2" full>
272 Invite Group <Blinky />
273 </Grid.Item>
274 )}
275 {/* {inviteButton} */}
276 <Grid.Item full as={Flex} justify="between">
277 <Card
278 title="ID"
279 subtitle="Identity and security settings"
280 icon={<Icon icon="User" />}
281 onClick={goUrbitID}
282 disabled={!canManage}
283 />
284 </Grid.Item>
285 <Grid.Item full as={Flex} justify="between">
286 <Card
287 title="OS"
288 subtitle="Urbit OS Settings"
289 icon={<Icon icon="Server" />}
290 onClick={goUrbitOS}
291 disabled={!canManage}
292 />
293 </Grid.Item>
294 {isParent && (
295 <>
296 <Grid.Item
297 full
298 as={ForwardButton}
299 disabled={!canSpawn}
300 onClick={goIssuePoint}>
301 Issue Point
302 </Grid.Item>
303 <Grid.Divider />
304 </>
305 )}
306 {senateButton}
307 </Grid>
308 </View>
309 );