on master help sign in
point-view: refactor component state and lifecycle

- extract shared Network Keys logic to custom hook - refactor API calls with useEffect hooks - extract InviteForm to separate component - clean up extraneous odds and ends

commit479879e7a1512dfcd110f05ee609cb9a9e93a2cb authortomholford <tomholford@users.noreply.github.com> dateSun 26 Sep 2021, 16:37 (5 years ago) parent64fd530
6 files changed+186−201
src/components/L2/Dropdowns/AccountsDropdown.tsx+9−18 src/lib/useHasNetworkKeysSet.tsx+30 src/lib/useRoller.ts+8−8 src/views/Login/Ticket.js+2−6 src/views/Point/InviteForm.tsx+92 src/views/Point/Point.js → src/views/Point/Point.tsx+45−169
src/components/L2/Dropdowns/AccountsDropdown.tsx+9−18
@@ -1,6 +1,5 @@
1−import React, { useCallback, useState } from 'react';
1+import { useCallback, useMemo, useState } from 'react';
22import * as ob from 'urbit-ob';
3−import * as need from 'lib/need';
43import { Icon, Row, Box, Button } from '@tlon/indigo-react';
54import { Just } from 'folktale/maybe';
65
@@ -14,7 +13,6 @@ import { useHistory } from 'store/history';
1413import { usePointCursor } from 'store/pointCursor';
1514import { usePointCache } from 'store/pointCache';
1615import { clearInvitesStorage } from 'store/storage/roller';
17−import { useRollerStore } from 'store/roller';
1816
1917import { PointLayer } from 'lib/types/PointLayer';
2018import { abbreviateAddress } from 'lib/utils/address';
@@ -22,8 +20,8 @@ import { abbreviateAddress } from 'lib/utils/address';
2220import Dropdown from './Dropdown';
2321import './AccountsDropdown.scss';
2422import Modal from '../Modal';
25−import { convertToInt } from 'lib/convertToInt';
26−import { isPlanet } from 'lib/utils/point';
23+import { useHasNetworkKeysSet } from 'lib/useHasNetworkKeysSet';
24+import { useRollerStore } from 'store/roller';
2725
2826interface AccountsDropdownProps {
2927 showMigrate: boolean;
@@ -35,19 +33,12 @@ const AccountsDropdown = ({ showMigrate = false }: AccountsDropdownProps) => {
3533 const { push, names, reset }: any = useHistory();
3634 const { setPointCursor }: any = usePointCursor();
3735 const { controlledPoints }: any = usePointCache();
38− const { currentL2 } = useRollerStore();
39− const { getDetails }: any = usePointCache();
40− const { pointCursor }: any = usePointCursor();
41− const point = pointCursor.getOrElse(null);
42−
43− let networkKeysNotSet = true;
36+ const { currentL2 }: any = useRollerStore();
4437
45− // if (point) {
46− // const details = need.details(getDetails(point));
47− // const isStarOrGalaxy = !isPlanet(point);
48− // const networkRevision = convertToInt(details.keyRevisionNumber, 10);
49− // networkKeysNotSet = !currentL2 && isStarOrGalaxy && networkRevision === 0;
50− // }
38+ const networkKeysSet = useHasNetworkKeysSet();
39+ const showMigrateModal = useMemo(() => {
40+ return showMigrate || (!currentL2 && networkKeysSet);
41+ }, [currentL2, networkKeysSet, showMigrate]);
5142
5243 const [showModal, setShowModal] = useState(false);
5344
@@ -115,7 +106,7 @@ const AccountsDropdown = ({ showMigrate = false }: AccountsDropdownProps) => {
115106 })}
116107 </Box>
117108 <Box className="divider" />
118− {(showMigrate || (!currentL2 && !networkKeysNotSet)) && (
109+ {showMigrateModal && (
119110 <Row className="entry" onClick={() => setShowModal(true)}>
120111 <Box>Migrate</Box>
121112 <Row className="layer-migration">
src/lib/useHasNetworkKeysSet.tsxnew file+30
@@ -0,0 +1,30 @@
1+import { useEffect, useMemo, useState } from 'react';
2+import * as need from 'lib/need';
3+import { usePointCache } from 'store/pointCache';
4+import { usePointCursor } from 'store/pointCursor';
5+import { useRollerStore } from 'store/roller';
6+import { convertToInt } from 'lib/convertToInt';
7+import { isPlanet } from 'lib/utils/point';
8+
9+export const useHasNetworkKeysSet = () => {
10+ const { pointCursor } = usePointCursor();
11+ const { getDetails } = usePointCache();
12+ const { currentL2 } = useRollerStore();
13+ const point = pointCursor.getOrElse(null);
14+ const [networkKeysSet, setNetworkKeysSet] = useState<boolean>(true);
15+
16+ useEffect(() => {
17+ if (point) {
18+ const details = need.details(getDetails(point));
19+ const isStarOrGalaxy = !isPlanet(point);
20+ const networkRevision = convertToInt(details.keyRevisionNumber, 10);
21+ const networkKeysNotSet =
22+ !currentL2 && isStarOrGalaxy && networkRevision === 0;
23+ setNetworkKeysSet(!networkKeysNotSet);
24+ }
25+ }, [currentL2, getDetails, point, pointCursor]);
26+
27+ return useMemo(() => {
28+ return networkKeysSet;
29+ }, [networkKeysSet]);
30+};
src/lib/useRoller.ts+8−8
@@ -401,14 +401,14 @@ export default function useRoller() {
401401 }
402402 },
403403 [
404− // api,
405− // pointCursor,
406− // setInvites,
407− // setPendingTransactions,
408− // authToken,
409− // contracts,
410− // controlledPoints,
411− // getDetails,
404+ api,
405+ authToken,
406+ contracts,
407+ controlledPoints,
408+ getDetails,
409+ pointCursor,
410+ setInvites,
411+ setPendingTransactions,
412412 ]
413413 );
414414
src/views/Login/Ticket.js+2−6
@@ -1,6 +1,5 @@
1−import React, { useCallback, useMemo, useRef } from 'react';
1+import { useCallback, useMemo, useRef } from 'react';
22import { Just } from 'folktale/maybe';
3−import * as azimuth from 'azimuth-js';
43import * as kg from 'urbit-key-generation';
54import {
65 Grid,
@@ -11,11 +10,9 @@ import {
1110} from 'indigo-react';
1211import { FORM_ERROR } from 'final-form';
1312
14−import { useNetwork } from 'store/network';
1513import { useWallet } from 'store/wallet';
1614import { usePointCursor } from 'store/pointCursor';
1715
18−import * as need from 'lib/need';
1916import { urbitWalletFromTicket } from 'lib/wallet';
2017import { WALLET_TYPES } from 'lib/constants';
2118import useImpliedPoint from 'lib/useImpliedPoint';
@@ -92,12 +89,11 @@ function TicketInputAccessory({ name }) {
9289export default function Ticket({ className, goHome }) {
9390 useLoginView(WALLET_TYPES.TICKET);
9491
95− const { contracts } = useNetwork();
9692 const { setUrbitWallet } = useWallet();
9793 const { setPointCursor } = usePointCursor();
9894 const impliedPoint = useImpliedPoint();
9995 const didWarn = useRef(false);
100− const { api, getPoints } = useRoller();
96+ const { getPoints } = useRoller();
10197
10298 const validateForm = useCallback((values, errors) => {
10399 didWarn.current = false;
src/views/Point/InviteForm.tsxnew file+92
@@ -0,0 +1,92 @@
1+import cn from 'classnames';
2+import BarGraph from 'components/BarGraph';
3+import Chip from 'components/Chip';
4+import InviteSigilList from 'components/InviteSigilList';
5+import { Grid, Flex } from 'indigo-react';
6+import { Button } from '@tlon/indigo-react';
7+import Inviter from 'views/Invite/Inviter';
8+
9+export const InviteForm = ({
10+ showInviteForm,
11+ setShowInviteForm,
12+ acceptedInvites,
13+ acceptedPoints,
14+ availableInvites,
15+ goCohort,
16+ pendingPoints,
17+ sentInvites,
18+ showInvites,
19+}) => {
20+ const _totalInvites =
21+ sentInvites.getOrElse(0) + availableInvites.getOrElse(0);
22+ const _pendingInvites = pendingPoints.getOrElse([]).length;
23+
24+ return (
25+ <>
26+ <Grid.Item cols={[1, 11]}>
27+ Invite Group
28+ <br />
29+ </Grid.Item>
30+
31+ <Grid.Item
32+ className={cn('t-right underline pointer-hover', {
33+ gray4: sentInvites.getOrElse(0) === 0,
34+ })}
35+ onClick={goCohort}
36+ cols={[11, 13]}>
37+ View
38+ </Grid.Item>
39+ <Grid.Item full>
40+ <Flex align="center">
41+ <Flex.Item>
42+ {acceptedInvites.getOrElse(0)} / {_totalInvites}
43+ </Flex.Item>
44+ {_pendingInvites > 0 && (
45+ <Flex.Item as={Chip} className="bg-yellow1 yellow4">
46+ {_pendingInvites} pending
47+ </Flex.Item>
48+ )}
49+ </Flex>
50+ </Grid.Item>
51+
52+ {showInvites && (
53+ <>
54+ <Grid.Item
55+ full
56+ as={BarGraph}
57+ available={availableInvites}
58+ sent={sentInvites}
59+ accepted={acceptedInvites}
60+ />
61+ <Grid.Item
62+ full
63+ as={InviteSigilList}
64+ pendingPoints={pendingPoints}
65+ acceptedPoints={acceptedPoints}
66+ />
67+ </>
68+ )}
69+ {!showInvites && (
70+ <>
71+ <Grid.Item full className="b-gray4 b-dotted b1 self-center">
72+ <div className="p4 pv8 t-center gray4">
73+ Start your invite group by adding members
74+ </div>
75+ </Grid.Item>
76+ </>
77+ )}
78+ {!showInviteForm && availableInvites.getOrElse(0) > 0 && (
79+ <Grid.Item
80+ full
81+ solid
82+ as={Button}
83+ center
84+ onClick={() => setShowInviteForm(true)}>
85+ Add Members
86+ </Grid.Item>
87+ )}
88+ {showInviteForm && <Inviter />}
89+ <Grid.Item full className="mb2" />
90+ </>
91+ );
92+};
src/views/Point/Point.js → src/views/Point/Point.tsxrenamed+45−169
@@ -1,5 +1,4 @@
1−import React, { useCallback, useEffect, useState } from 'react';
2−import cn from 'classnames';
1+import { useCallback, useEffect, useMemo, useState } from 'react';
32import { azimuth } from 'azimuth-js';
43import { Just } from 'folktale/maybe';
54import { Grid, Flex } from 'indigo-react';
@@ -13,9 +12,6 @@ import View from 'components/View';
1312import Greeting from 'components/Greeting';
1413import Passport from 'components/Passport';
1514import Blinky from 'components/Blinky';
16−import BarGraph from 'components/BarGraph';
17−import Chip from 'components/Chip';
18−import InviteSigilList from 'components/InviteSigilList';
1915import { ForwardButton } from 'components/Buttons';
2016import L2PointHeader from 'components/L2/Headers/L2PointHeader';
2117import LayerIndicator from 'components/L2/LayerIndicator';
@@ -23,185 +19,67 @@ import Card from 'components/L2/Card';
2319
2420import * as need from 'lib/need';
2521import useInvites from 'lib/useInvites';
26−import { useSyncExtras } from 'lib/useSyncPoints';
2722import useCurrentPermissions from 'lib/useCurrentPermissions';
2823import { useLocalRouter } from 'lib/LocalRouter';
29−import Inviter from 'views/Invite/Inviter';
3024import useRoller from 'lib/useRoller';
3125
3226import './Point.scss';
3327import { isL2 } from 'lib/utils/roller';
3428import { isDevelopment } from 'lib/flags';
35−import { convertToInt } from 'lib/convertToInt';
3629import { usePointCache } from 'store/pointCache';
3730import Modal from 'components/L2/Modal';
3831import { isPlanet } from 'lib/utils/point';
39−
40−function 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;
54−
55− return (
56− <>
57− <Grid.Item cols={[1, 11]}>
58− Invite Group
59− <br />
60− </Grid.Item>
61−
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>
82−
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− );
123−}
32+import { useHasNetworkKeysSet } from 'lib/useHasNetworkKeysSet';
33+import { InviteForm } from './InviteForm';
12434
12535export default function Point() {
12636 const { pop, push, names } = useLocalRouter();
127− const { wallet, authToken } = useWallet();
37+ const { wallet } = useWallet();
12838 const { pointCursor } = usePointCursor();
12939 const point = need.point(pointCursor);
130− const { getDetails } = usePointCache();
131− // sync the current cursor
132− useSyncExtras([point]);
133−
40+ const { syncExtras } = usePointCache();
13441 const { api, getInvites } = useRoller();
13542 const {
13643 pendingTransactions,
13744 nextRoll,
13845 invites,
13946 setCurrentPoint,
140− setPendingTransactions,
141− currentL2,
14247 setNonces,
14348 increaseNonce,
144− nonces,
14549 } = useRollerStore();
146−
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;
158−
159− const [showModal, setShowModal] = useState(networkKeysNotSet);
160−
161− // useEffect(() => {
162− // if (networkKeysNotSet) {
163− // setShowModal(true);
164− // }
165− // }, [networkKeysNotSet]);
166−
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);
177−
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− };
186−
187− getTransactions();
188− };
189−
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− );
50+ const networkKeysSet = useHasNetworkKeysSet();
51+ const [showModal, setShowModal] = useState(!networkKeysSet);
52+
53+ const loadL1Info = useCallback(async () => {
54+ await syncExtras(point);
55+ }, [point, syncExtras]);
56+
57+ const loadL2Info = useCallback(async () => {
58+ const getTransactions = async () => {
59+ const pointInfo = await api.getPoint(Number(point));
60+ const pendingTxs = await api.getPendingByShip(Number(point));
61+ if (isDevelopment) {
62+ console.log('POINT INFO', pointInfo);
63+ }
64+ setNonces(point, pointInfo.ownership);
65+
66+ for (let index = 0; index < pendingTxs.length; index++) {
67+ const proxy = pendingTxs[index].rawTx?.from?.proxy;
68+ console.log(pointInfo.ownership, proxy);
69+ increaseNonce(point, proxy);
70+ }
71+ setCurrentPoint(pointInfo);
72+ await getInvites(isL2(pointInfo.dominion));
73+ };
74+
75+ await getTransactions();
76+ }, [api, point, setCurrentPoint, increaseNonce, getInvites, setNonces]);
77+
78+ useEffect(() => {
79+ loadL1Info();
80+ loadL2Info();
81+ // eslint-disable-next-line react-hooks/exhaustive-deps
82+ }, [point]);
20583
20684 const { isParent, canManage, canSpawn, canVote } = useCurrentPermissions();
20785
@@ -222,8 +100,6 @@ export default function Point() {
222100 const hasInvites = showInvites || availableInvites.getOrElse(0) !== 0;
223101
224102 const loadedInvites = Just.hasInstance(availableInvites);
225− //
226− // availableInvites.getOrElse(0) === 0
227103
228104 const goSenate = useCallback(() => push(names.SENATE), [push, names]);
229105
@@ -262,13 +138,13 @@ export default function Point() {
262138 );
263139 })();
264140
265− // sync the current cursor
266− useSyncExtras([point]);
267−
268141 const address = need.addressFromWallet(wallet);
269− const numPending = pendingTransactions.filter(
270− ({ rawTx }) => rawTx?.tx?.type === 'spawn'
271− ).length;
142+
143+ const numPending = useMemo(() => {
144+ return pendingTransactions.filter(
145+ ({ rawTx }) => rawTx?.tx?.type === 'spawn'
146+ ).length;
147+ }, [pendingTransactions]);
272148
273149 return (
274150 <View
@@ -280,7 +156,7 @@ export default function Point() {
280156 <L2PointHeader
281157 hideTimer={!!numPending}
282158 numInvites={invites.length}
283− hideInvites={networkKeysNotSet}
159+ hideInvites={!networkKeysSet}
284160 />
285161 }>
286162 <Greeting point={point} />