@@ -0,0 +1,163 @@
1+import React, { useCallback, useEffect, useState } from 'react';
2+import * as azimuth from 'azimuth-js';
3+import Maybe from 'folktale/maybe';
4+import { useTicketInput } from 'components/Inputs';
5+import { Grid, Input, H4, ErrorText } from 'indigo-react';
6+
7+import View from 'components/View';
8+import { ForwardButton, OfflineButton } from 'components/Buttons';
9+import Passport from 'components/Passport';
10+
11+import { useHistory } from 'store/history';
12+
13+import * as need from 'lib/need';
14+import { ROUTE_NAMES } from 'lib/routeNames';
15+import { useSyncKnownPoints } from 'lib/useSyncPoints';
16+import FooterButton from 'components/FooterButton';
17+import Blinky from 'components/Blinky';
18+import { DEFAULT_HD_PATH, walletFromMnemonic } from 'lib/wallet';
19+import { useNetwork } from 'store/network';
20+import { generateWallet } from 'lib/invite';
21+import { generateTemporaryOwnershipWallet } from 'lib/walletgen';
22+import { useActivateFlow } from './ActivateFlow';
23+import { useLocalRouter } from 'lib/LocalRouter';
24+
25+export default function ActivateCode() {
26+ const history = useHistory();
27+ const { names, push } = useLocalRouter();
28+ const { contracts } = useNetwork();
29+ const [generalError, setGeneralError] = useState();
30+ const [deriving, setDeriving] = useState(false);
31+ const {
32+ derivedWallet,
33+ setDerivedWallet,
34+ setInviteWallet,
35+ derivedPoint,
36+ setDerivedPoint,
37+ } = useActivateFlow();
38+
39+ const ticketInput = useTicketInput({
40+ name: 'ticket',
41+ label: 'Code',
42+ autoFocus: true,
43+ });
44+ const validTicket = ticketInput.pass;
45+
46+ const goToLogin = useCallback(() => history.popAndPush(ROUTE_NAMES.LOGIN), [
47+ history,
48+ ]);
49+ const goToDisclaimer = useCallback(() => push(names.DISCLAIMER), [
50+ names,
51+ push,
52+ ]);
53+
54+ const pass = derivedWallet.matchWith({
55+ Nothing: () => false,
56+ Just: () => true,
57+ });
58+
59+ useEffect(() => {
60+ if (validTicket) {
61+ const _contracts = need.contracts(contracts);
62+
63+ setDeriving(true);
64+ // when the ticket becomes valid, derive the point
65+ (async () => {
66+ const { seed } = await generateTemporaryOwnershipWallet(
67+ ticketInput.data
68+ );
69+
70+ //TODO isn't all this accessible in the ownership object?
71+ const inviteWallet = walletFromMnemonic(
72+ seed,
73+ DEFAULT_HD_PATH
74+ ).matchWith({
75+ Just: w => w.value,
76+ Nothing: () => {
77+ setGeneralError('Derivation Error: invalid derived seed.');
78+ throw new Error();
79+ },
80+ });
81+
82+ const incoming = await azimuth.azimuth.getTransferringFor(
83+ _contracts,
84+ inviteWallet.address
85+ );
86+
87+ let realPoint = Maybe.Nothing();
88+ let realWallet = Maybe.Nothing();
89+
90+ if (incoming.length > 0) {
91+ let pointNum = parseInt(incoming[0], 10);
92+ realPoint = Maybe.Just(pointNum);
93+
94+ const genWallet = await generateWallet(pointNum);
95+
96+ realWallet = Maybe.Just(genWallet);
97+ if (incoming.length > 1) {
98+ setGeneralError(
99+ 'This invite code has multiple points available.\n' +
100+ "Once you've activated this point, activate the next with the same process."
101+ );
102+ }
103+ } else {
104+ setGeneralError(
105+ 'Invite code has no claimable point.\n' +
106+ 'Check your invite code and try again?'
107+ );
108+ }
109+
110+ setDerivedPoint(realPoint);
111+ setDerivedWallet(realWallet);
112+ setInviteWallet(Maybe.Just(inviteWallet));
113+ setDeriving(false);
114+ })();
115+ } else {
116+ setGeneralError(false);
117+ }
118+ }, [
119+ validTicket,
120+ contracts,
121+ ticketInput.data,
122+ setDerivedPoint,
123+ setDerivedWallet,
124+ setInviteWallet,
125+ ]);
126+
127+ // when we know the derived point, ensure we have the data to display it
128+ useSyncKnownPoints([derivedPoint.getOrElse(null)].filter(p => p !== null));
129+
130+ return (
131+ <View>
132+ <Grid>
133+ <Grid.Item as={Passport} point={derivedPoint} full />
134+ <Grid.Item as={H4} className="mt3 mb2" full>
135+ Activate
136+ </Grid.Item>
137+ <Grid.Item as={Input} {...ticketInput} full />
138+ {generalError && (
139+ <Grid.Item full>
140+ <ErrorText>{generalError}</ErrorText>{' '}
141+ </Grid.Item>
142+ )}
143+ <Grid.Item
144+ as={ForwardButton}
145+ className="mt4"
146+ disabled={!pass || deriving}
147+ accessory={deriving ? <Blinky /> : null}
148+ onClick={goToDisclaimer}
149+ solid
150+ full>
151+ {deriving && 'Deriving...'}
152+ {!deriving && 'Go'}
153+ </Grid.Item>
154+ <Grid.Divider className="mt3" />
155+ <Grid.Item as={ForwardButton} onClick={goToLogin} full>
156+ Login
157+ </Grid.Item>
158+ <Grid.Divider />
159+ </Grid>
160+ <FooterButton as={OfflineButton} />
161+ </View>
162+ );
163+}