@@ -1,185 +1,51 @@
1−import React, { useEffect, useCallback, useMemo, useState } from 'react';
2−import { Grid, Button } from 'indigo-react';
3−import * as azimuth from 'azimuth-js';
4−import {
5− Icon,
6− Row,
7− StatelessTextInput,
8− Box,
9− LoadingSpinner,
10−} from '@tlon/indigo-react';
1+import { useCallback, useState } from 'react';
2+import { Button } from 'indigo-react';
3+import { Icon, Row, Box, LoadingSpinner } from '@tlon/indigo-react';
114
12−import { usePointCache } from 'store/pointCache';
13−import { useWallet } from 'store/wallet';
14−import { useNetwork } from 'store/network';
155import { useRollerStore } from 'store/rollerStore';
16−import { getStoredInvites } from 'store/storage/roller';
17−
186import { useLocalRouter } from 'lib/LocalRouter';
19−import * as need from 'lib/need';
20−import useRoller from 'lib/useRoller';
217import {
228 generateUrl,
239 generateUrlAbbreviation,
2410 generateCsvLine,
2511 generateCsvName,
2612} from 'lib/utils/invite';
27−import {
28− DEFAULT_NUM_INVITES,
29− ETH_ZERO_ADDR,
30− INVITES_PER_PAGE,
31− DEFAULT_CSV_NAME,
32−} from 'lib/constants';
33−import { isPlanet } from 'lib/utils/point';
34−import { generateInviteWallet } from 'lib/utils/roller';
13+import { INVITES_PER_PAGE, DEFAULT_CSV_NAME } from 'lib/constants';
3514import { useTimerStore } from 'store/timerStore';
3615import { Invite } from 'lib/types/Invite';
3716import { ddmmmYYYY } from 'lib/utils/date';
38−import { isExternalWallet } from 'lib/utils/wallet';
3917
40−import { useIssueChild } from 'views/Point/IssueChild';
4118import View from 'components/View';
4219import L2BackHeader from 'components/L2/Headers/L2BackHeader';
4320import Window from 'components/L2/Window/Window';
4421import HeaderPane from 'components/L2/Window/HeaderPane';
4522import BodyPane from 'components/L2/Window/BodyPane';
46−import LoadingOverlay from 'components/L2/LoadingOverlay';
4723import CopiableWithTooltip from 'components/copiable/CopiableWithTooltip';
4824import Paginator from 'components/L2/Paginator';
49−import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
5025
5126import './Cohort.scss';
5227import Modal from 'components/L2/Modal';
53−import {
54− InviteGeneratingStatus,
55− useInvites,
56− useInviteStore,
57−} from './useInvites';
58−import { GeneratingModal } from './GeneratingModal';
59−
60−interface L1Invite {
61− ticket: string;
62− planet: number;
63−}
28+import { useInvites, useInviteStore } from './useInvites';
6429
6530export default function InviteCohort() {
66− const { pop }: any = useLocalRouter();
67− const { authToken, walletType }: any = useWallet();
31+ const { names, pop, push } = useLocalRouter();
6832 const { point, pendingTransactions, nextQuotaTime } = useRollerStore();
6933
7034 const { nextRoll } = useTimerStore();
7135
72− const { ls, getPendingTransactions, getAndUpdatePoint } = useRoller();
73− const { contracts }: any = useNetwork();
74− const { syncControlledPoints }: any = usePointCache();
75−
76− const { invites, generatingNum, setInvites } = useInviteStore();
77− const { getInvites, generateInviteCodes } = useInvites();
36+ const { invites, generatingNum } = useInviteStore();
37+ const { getInvites } = useInvites();
7838 const currentL2 = Boolean(point.isL2Spawn);
7939
80− const _contracts = need.contracts(contracts);
81−
82− const [numInvites, setNumInvites] = useState(
83− currentL2 ? DEFAULT_NUM_INVITES : 1
84− );
85− const [showInviteForm, setShowInviteForm] = useState(false);
86− const [generatingStatus, setGeneratingStatus] = useState<
87− InviteGeneratingStatus
88− >('initial');
8940 const [loading, setLoading] = useState(false);
9041 const [showCsvModal, setShowCsvModal] = useState(false);
9142 const [csvElement, setCsvElement] = useState<HTMLAnchorElement | null>();
9243 // TODO: do we need to read this error? currently unused
9344 const [error, setError] = useState('');
94− const [l1Invite, setL1Invite] = useState<L1Invite | null>(null);
9545 const [page, setPage] = useState(0);
9646
97− const { construct, unconstruct, completed, bind } = useIssueChild();
9847 const invitePoints = invites[point.value];
9948
100− // Set up the invite spawn if on L1
101− useEffect(() => {
102− const _authToken = authToken.getOrElse(null);
103−
104− if (!_authToken) {
105− return;
106− }
107−
108− const setUpInvite = async () => {
109− setLoading(true);
110− const possiblePoints = await azimuth.azimuth.getUnspawnedChildren(
111− _contracts,
112− point
113− );
114−
115− let invitePoint;
116− for (let i = 0; i < possiblePoints.length; i++) {
117− const p = possiblePoints[i];
118− if (
119− !invitePoints.find(({ planet }) => planet === p) &&
120− isPlanet(p) &&
121− (await azimuth.azimuth.getOwner(_contracts, p)) === ETH_ZERO_ADDR
122− ) {
123− invitePoint = p;
124− break;
125− }
126− }
127−
128− if (invitePoint) {
129− useInviteStore.setState({ generatingNum: 1 });
130− const { ticket, inviteWallet } = await generateInviteWallet(
131− invitePoint,
132− _authToken
133− );
134−
135− construct(invitePoint, inviteWallet.ownership.keys.address);
136− setL1Invite({ ticket, planet: invitePoint });
137− } else {
138− setError('No available planets');
139− }
140−
141− setLoading(false);
142− };
143−
144− if (!currentL2 && _contracts && point && _authToken && showInviteForm) {
145− setUpInvite();
146− } else if (!currentL2 && !showInviteForm) {
147− unconstruct();
148− }
149− }, [
150− currentL2,
151− point,
152− _contracts,
153− authToken,
154− construct,
155− showInviteForm,
156− unconstruct,
157− invitePoints,
158− ]);
159−
160− useEffect(() => {
161− const storedInvites = getStoredInvites(ls);
162−
163− if (
164− !currentL2 ||
165− !completed ||
166− !l1Invite ||
167− storedInvites[l1Invite.planet]
168− ) {
169− return;
170− }
171−
172− syncControlledPoints();
173− setInvites(point.value, [
174− ...invitePoints,
175− { ...l1Invite, hash: '', owner: '' },
176− ]);
177− setShowInviteForm(false);
178− setL1Invite(null);
179− unconstruct();
180− pop();
181− }, [completed, currentL2, point]); // eslint-disable-line react-hooks/exhaustive-deps
182−
18349 const invitesToDisplay = invitePoints.slice(
18450 page * INVITES_PER_PAGE,
18551 (page + 1) * INVITES_PER_PAGE
@@ -188,32 +54,6 @@ export default function InviteCohort() {
18854 const hasPending = Boolean(pendingTransactions.length);
18955 const hasInvites = Boolean(invitePoints.length);
19056
191− const createInvites = useCallback(async () => {
192− setGeneratingStatus('generating');
193− try {
194− await generateInviteCodes(numInvites);
195− getPendingTransactions();
196− getAndUpdatePoint(point.value);
197− setGeneratingStatus('finished');
198− } catch (error) {
199− if (typeof error === 'object' && (error as Error)?.message) {
200− setError((error as Error).message);
201− } else if (typeof error === 'string') {
202− setError(error);
203− } else {
204− setError('Generating invites failed');
205− }
206− setGeneratingStatus('errored');
207− }
208− }, [
209− generateInviteCodes,
210− numInvites,
211− getPendingTransactions,
212− getAndUpdatePoint,
213− point,
214− setGeneratingStatus,
215− ]);
216−
21757 const generateCsv = useCallback(() => {
21858 const generateCsvElement = async () => {
21959 setLoading(true);
@@ -252,8 +92,6 @@ export default function InviteCohort() {
25292 setPage(page + 1);
25393 }, [page]);
25494
255− const overQuota = numInvites > point.l2Quota && numInvites > 0;
256−
25795 const getContent = () => {
25896 if (hasInvites) {
25997 return (
@@ -325,118 +163,6 @@ export default function InviteCohort() {
325163 </h5>
326164 );
327165
328− if (showInviteForm) {
329− const generateDisabled = loading || overQuota;
330− const showTxnNote = isExternalWallet(walletType);
331−
332− return (
333− <View
334− pop={() => setShowInviteForm(false)}
335− className="cohort show-invite-form"
336− hideBack
337− header={<L2BackHeader back={() => setShowInviteForm(false)} />}>
338− <Window>
339− <HeaderPane>
340− <h5>Generate Invite Codes</h5>
341− </HeaderPane>
342− <BodyPane>
343− <Box className="upper">
344− {currentL2 ? (
345− <Row className="points-input">
346− I want to generate
347− <StatelessTextInput
348− className={`input-box ${overQuota ? 'over-quota' : ''}`}
349− value={numInvites}
350− maxLength={3}
351− onChange={e => {
352− const target = e.target as HTMLInputElement;
353− setNumInvites(Number(target.value.replace(/\D/g, '')));
354− }}
355− />
356− planet invite code{numInvites > 1 ? 's' : ''}
357− </Row>
358− ) : (
359− <Box className="migration-prompt">
360− ETH fees are very high currently. You can spawn invites for
361− free after{' '}
362− <span className="migrate" onClick={() => null}>
363− {/* TODO: change this to navigate to migration */}
364− migrating this star to L2.
365− </span>
366− </Box>
367− )}
368− {currentL2 && (
369− <Box className="mb4" lineHeight="1.4em">
370− You can generate up to
371− <strong>{` ${point.l2Quota} `}</strong>
372− invites. You will be able to generate another
373− <strong>{` ${point.l2Allowance}`}</strong> invites on
374− <strong>{` ${ddmmmYYYY(nextQuotaTime)}`}</strong>.
375− </Box>
376− )}
377− {showTxnNote && (
378− <Box className="mb6" lineHeight="1.4em">
379− Note: you will have to sign <strong>4</strong> transactions
380− per invite, for a total of <strong>{4 * numInvites}</strong>{' '}
381− signatures.
382− </Box>
383− )}
384− </Box>
385− {/* <Inviter /> */}
386− <Box className="lower">
387− {currentL2 && (
388− <Row className="next-roll">
389− <span>Next Roll in</span>
390− <span className="timer">{nextRoll}</span>
391− </Row>
392− )}
393− {currentL2 ? (
394− // Ignoring for deprecated `indigo-react` component
395− //@ts-ignore
396− <Button
397− as={'button'}
398− className={`generate-codes ${
399− generateDisabled ? 'disabled' : ''
400− }`}
401− disabled={generateDisabled}
402− center
403− onClick={createInvites}>
404− {loading
405− ? `Generating ${numInvites} invites...`
406− : overQuota
407− ? `You can only generate ${point.l2Quota} codes`
408− : `Generate Invite Code${
409− currentL2 ? `s (${numInvites})` : ''
410− }`}
411− </Button>
412− ) : (
413− <Grid.Item
414− full
415− as={InlineEthereumTransaction}
416− label="Generate Invite Code"
417− {...bind}
418− onReturn={() => pop()}
419− />
420− )}
421− </Box>
422− </BodyPane>
423− </Window>
424− <GeneratingModal
425− status={generatingStatus}
426− current={generatingNum}
427− total={numInvites}
428− error={error}
429− hide={() => {
430− setGeneratingStatus('initial');
431− if (generatingStatus !== 'errored') {
432− pop();
433− }
434− }}
435− />
436− </View>
437− );
438− }
439−
440166 const showGenerateButton = !hasPending && !hasInvites && point.canSpawn;
441167 const showAddMoreButton =
442168 (hasInvites || hasPending) && point.canSpawn && point.l2Quota > 0;
@@ -473,7 +199,7 @@ export default function InviteCohort() {
473199 <Button
474200 className="generate-button ph4"
475201 center
476− onClick={() => setShowInviteForm(true)}>
202+ onClick={() => push(names.GENERATE_INVITES)}>
477203 Generate Codes
478204 </Button>
479205 )}
@@ -490,7 +216,7 @@ export default function InviteCohort() {
490216 </Window>
491217 {showAddMoreButton ? (
492218 <Button
493− onClick={() => setShowInviteForm(true)}
219+ onClick={() => push(names.GENERATE_INVITES)}
494220 className="add-more"
495221 //@ts-ignore
496222 accessory={<Icon icon="ChevronEast" />}>