@@ -1,185 +1,51 @@
1−import React, { useEffect, useCallback, useMemo, useState } from 'react';
1+import { useCallback, useState } from 'react';
2−import { Grid, Button } from 'indigo-react';
2+import { Button } from 'indigo-react';
3−import * as azimuth from 'azimuth-js';
10−} from '@tlon/indigo-react';
3+import { Icon, Row, Box, LoadingSpinner } from '@tlon/indigo-react';
12−import { usePointCache } from 'store/pointCache';
13−import { useWallet } from 'store/wallet';
14−import { useNetwork } from 'store/network';
15import { useRollerStore } from 'store/rollerStore';
5import { useRollerStore } from 'store/rollerStore';
16−import { getStoredInvites } from 'store/storage/roller';
18import { useLocalRouter } from 'lib/LocalRouter';
6import { useLocalRouter } from 'lib/LocalRouter';
19−import * as need from 'lib/need';
20−import useRoller from 'lib/useRoller';
22 generateUrl,
8 generateUrl,
23 generateUrlAbbreviation,
9 generateUrlAbbreviation,
24 generateCsvLine,
10 generateCsvLine,
25 generateCsvName,
11 generateCsvName,
26} from 'lib/utils/invite';
12} from 'lib/utils/invite';
30− INVITES_PER_PAGE,
13+import { INVITES_PER_PAGE, DEFAULT_CSV_NAME } from 'lib/constants';
32−} from 'lib/constants';
33−import { isPlanet } from 'lib/utils/point';
34−import { generateInviteWallet } from 'lib/utils/roller';
35import { useTimerStore } from 'store/timerStore';
14import { useTimerStore } from 'store/timerStore';
36import { Invite } from 'lib/types/Invite';
15import { Invite } from 'lib/types/Invite';
37import { ddmmmYYYY } from 'lib/utils/date';
16import { ddmmmYYYY } from 'lib/utils/date';
38−import { isExternalWallet } from 'lib/utils/wallet';
40−import { useIssueChild } from 'views/Point/IssueChild';
41import View from 'components/View';
18import View from 'components/View';
42import L2BackHeader from 'components/L2/Headers/L2BackHeader';
19import L2BackHeader from 'components/L2/Headers/L2BackHeader';
43import Window from 'components/L2/Window/Window';
20import Window from 'components/L2/Window/Window';
44import HeaderPane from 'components/L2/Window/HeaderPane';
21import HeaderPane from 'components/L2/Window/HeaderPane';
45import BodyPane from 'components/L2/Window/BodyPane';
22import BodyPane from 'components/L2/Window/BodyPane';
46−import LoadingOverlay from 'components/L2/LoadingOverlay';
47import CopiableWithTooltip from 'components/copiable/CopiableWithTooltip';
23import CopiableWithTooltip from 'components/copiable/CopiableWithTooltip';
48import Paginator from 'components/L2/Paginator';
24import Paginator from 'components/L2/Paginator';
49−import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
51import './Cohort.scss';
26import './Cohort.scss';
52import Modal from 'components/L2/Modal';
27import Modal from 'components/L2/Modal';
54− InviteGeneratingStatus,
56− useInviteStore,
28+import { useInvites, useInviteStore } from './useInvites';
57−} from './useInvites';
58−import { GeneratingModal } from './GeneratingModal';
65export default function InviteCohort() {
30export default function InviteCohort() {
66− const { pop }: any = useLocalRouter();
31+ const { names, pop, push } = useLocalRouter();
67− const { authToken, walletType }: any = useWallet();
68 const { point, pendingTransactions, nextQuotaTime } = useRollerStore();
32 const { point, pendingTransactions, nextQuotaTime } = useRollerStore();
70 const { nextRoll } = useTimerStore();
34 const { nextRoll } = useTimerStore();
72− const { ls, getPendingTransactions, getAndUpdatePoint } = useRoller();
73− const { contracts }: any = useNetwork();
74− const { syncControlledPoints }: any = usePointCache();
76− const { invites, generatingNum, setInvites } = useInviteStore();
36+ const { invites, generatingNum } = useInviteStore();
77− const { getInvites, generateInviteCodes } = useInvites();
37+ const { getInvites } = useInvites();
78 const currentL2 = Boolean(point.isL2Spawn);
38 const currentL2 = Boolean(point.isL2Spawn);
80− const _contracts = need.contracts(contracts);
82− const [numInvites, setNumInvites] = useState(
83− currentL2 ? DEFAULT_NUM_INVITES : 1
85− const [showInviteForm, setShowInviteForm] = useState(false);
86− const [generatingStatus, setGeneratingStatus] = useState<
87− InviteGeneratingStatus
89 const [loading, setLoading] = useState(false);
40 const [loading, setLoading] = useState(false);
90 const [showCsvModal, setShowCsvModal] = useState(false);
41 const [showCsvModal, setShowCsvModal] = useState(false);
91 const [csvElement, setCsvElement] = useState<HTMLAnchorElement | null>();
42 const [csvElement, setCsvElement] = useState<HTMLAnchorElement | null>();
92 // TODO: do we need to read this error? currently unused
43 // TODO: do we need to read this error? currently unused
93 const [error, setError] = useState('');
44 const [error, setError] = useState('');
94− const [l1Invite, setL1Invite] = useState<L1Invite | null>(null);
95 const [page, setPage] = useState(0);
45 const [page, setPage] = useState(0);
97− const { construct, unconstruct, completed, bind } = useIssueChild();
98 const invitePoints = invites[point.value];
47 const invitePoints = invites[point.value];
100− // Set up the invite spawn if on L1
102− const _authToken = authToken.getOrElse(null);
108− const setUpInvite = async () => {
110− const possiblePoints = await azimuth.azimuth.getUnspawnedChildren(
116− for (let i = 0; i < possiblePoints.length; i++) {
117− const p = possiblePoints[i];
119− !invitePoints.find(({ planet }) => planet === p) &&
121− (await azimuth.azimuth.getOwner(_contracts, p)) === ETH_ZERO_ADDR
129− useInviteStore.setState({ generatingNum: 1 });
130− const { ticket, inviteWallet } = await generateInviteWallet(
135− construct(invitePoint, inviteWallet.ownership.keys.address);
136− setL1Invite({ ticket, planet: invitePoint });
138− setError('No available planets');
144− if (!currentL2 && _contracts && point && _authToken && showInviteForm) {
146− } else if (!currentL2 && !showInviteForm) {
161− const storedInvites = getStoredInvites(ls);
167− storedInvites[l1Invite.planet]
172− syncControlledPoints();
173− setInvites(point.value, [
175− { ...l1Invite, hash: '', owner: '' },
177− setShowInviteForm(false);
181− }, [completed, currentL2, point]); // eslint-disable-line react-hooks/exhaustive-deps
183 const invitesToDisplay = invitePoints.slice(
49 const invitesToDisplay = invitePoints.slice(
184 page * INVITES_PER_PAGE,
50 page * INVITES_PER_PAGE,
185 (page + 1) * INVITES_PER_PAGE
51 (page + 1) * INVITES_PER_PAGE
@@ -188,32 +54,6 @@ export default function InviteCohort() {
188 const hasPending = Boolean(pendingTransactions.length);
54 const hasPending = Boolean(pendingTransactions.length);
189 const hasInvites = Boolean(invitePoints.length);
55 const hasInvites = Boolean(invitePoints.length);
191− const createInvites = useCallback(async () => {
192− setGeneratingStatus('generating');
194− await generateInviteCodes(numInvites);
195− getPendingTransactions();
196− getAndUpdatePoint(point.value);
197− setGeneratingStatus('finished');
199− if (typeof error === 'object' && (error as Error)?.message) {
200− setError((error as Error).message);
201− } else if (typeof error === 'string') {
204− setError('Generating invites failed');
206− setGeneratingStatus('errored');
209− generateInviteCodes,
211− getPendingTransactions,
214− setGeneratingStatus,
217 const generateCsv = useCallback(() => {
57 const generateCsv = useCallback(() => {
218 const generateCsvElement = async () => {
58 const generateCsvElement = async () => {
219 setLoading(true);
59 setLoading(true);
@@ -252,8 +92,6 @@ export default function InviteCohort() {
252 setPage(page + 1);
92 setPage(page + 1);
253 }, [page]);
93 }, [page]);
255− const overQuota = numInvites > point.l2Quota && numInvites > 0;
257 const getContent = () => {
95 const getContent = () => {
258 if (hasInvites) {
96 if (hasInvites) {
@@ -325,118 +163,6 @@ export default function InviteCohort() {
328− if (showInviteForm) {
329− const generateDisabled = loading || overQuota;
330− const showTxnNote = isExternalWallet(walletType);
334− pop={() => setShowInviteForm(false)}
335− className="cohort show-invite-form"
337− header={<L2BackHeader back={() => setShowInviteForm(false)} />}>
340− <h5>Generate Invite Codes</h5>
343− <Box className="upper">
345− <Row className="points-input">
348− className={`input-box ${overQuota ? 'over-quota' : ''}`}
352− const target = e.target as HTMLInputElement;
353− setNumInvites(Number(target.value.replace(/\D/g, '')));
356− planet invite code{numInvites > 1 ? 's' : ''}
359− <Box className="migration-prompt">
360− ETH fees are very high currently. You can spawn invites for
362− <span className="migrate" onClick={() => null}>
363− {/* TODO: change this to navigate to migration */}
364− migrating this star to L2.
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>.
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>{' '}
386− <Box className="lower">
388− <Row className="next-roll">
389− <span>Next Roll in</span>
390− <span className="timer">{nextRoll}</span>
394− // Ignoring for deprecated `indigo-react` component
398− className={`generate-codes ${
399− generateDisabled ? 'disabled' : ''
401− disabled={generateDisabled}
403− onClick={createInvites}>
405− ? `Generating ${numInvites} invites...`
407− ? `You can only generate ${point.l2Quota} codes`
408− : `Generate Invite Code${
409− currentL2 ? `s (${numInvites})` : ''
415− as={InlineEthereumTransaction}
416− label="Generate Invite Code"
418− onReturn={() => pop()}
425− status={generatingStatus}
426− current={generatingNum}
430− setGeneratingStatus('initial');
431− if (generatingStatus !== 'errored') {
440 const showGenerateButton = !hasPending && !hasInvites && point.canSpawn;
166 const showGenerateButton = !hasPending && !hasInvites && point.canSpawn;
441 const showAddMoreButton =
167 const showAddMoreButton =
442 (hasInvites || hasPending) && point.canSpawn && point.l2Quota > 0;
168 (hasInvites || hasPending) && point.canSpawn && point.l2Quota > 0;
@@ -473,7 +199,7 @@ export default function InviteCohort() {
474 className="generate-button ph4"
200 className="generate-button ph4"
476− onClick={() => setShowInviteForm(true)}>
202+ onClick={() => push(names.GENERATE_INVITES)}>
477 Generate Codes
203 Generate Codes
478 </Button>
204 </Button>
@@ -490,7 +216,7 @@ export default function InviteCohort() {
490 </Window>
216 </Window>
491 {showAddMoreButton ? (
217 {showAddMoreButton ? (
493− onClick={() => setShowInviteForm(true)}
219+ onClick={() => push(names.GENERATE_INVITES)}
494 className="add-more"
220 className="add-more"
495 //@ts-ignore
221 //@ts-ignore
496 accessory={<Icon icon="ChevronEast" />}>
222 accessory={<Icon icon="ChevronEast" />}>