on f4fd597 help sign in
bridge/src/views/Activate/ActivateCode.js
178 lines 5.4 kB history raw
1import React, { useCallback, useMemo, useRef } from 'react';
2import { Just, Nothing } from 'folktale/maybe';
3import * as azimuth from 'azimuth-js';
4import { Grid, H4 } from 'indigo-react';
6import View from 'components/View';
7import { ForwardButton } from 'components/Buttons';
8import Passport from 'components/Passport';
10import { useHistory } from 'store/history';
12import * as need from 'lib/need';
13import { ROUTE_NAMES } from 'lib/routeNames';
14import FooterButton from 'components/FooterButton';
15import { DEFAULT_HD_PATH, walletFromMnemonic } from 'lib/wallet';
16import { useNetwork } from 'store/network';
17import { generateWallet } from 'lib/invite';
18import { generateTemporaryOwnershipWallet } from 'lib/walletgen';
19import { useActivateFlow } from './ActivateFlow';
20import { useLocalRouter } from 'lib/LocalRouter';
21import useImpliedTicket from 'lib/useImpliedTicket';
22import timeout from 'lib/timeout';
23import useHasDisclaimed from 'lib/useHasDisclaimed';
25import BridgeForm from 'form/BridgeForm';
26import SubmitButton from 'form/SubmitButton';
27import {
28 TicketInput,
29 hasErrors,
30 composeValidator,
31 buildTicketValidator,
32} from 'form/Inputs';
33import FormError from 'form/FormError';
34import { FORM_ERROR } from 'final-form';
36export default function ActivateCode() {
37 const history = useHistory();
38 const { names, push } = useLocalRouter();
39 const { contracts } = useNetwork();
40 const impliedTicket = useImpliedTicket();
41 const [hasDisclaimed] = useHasDisclaimed();
43 const cachedInviteWallet = useRef(Nothing());
44 const cachedPoint = useRef();
46 const {
47 setDerivedWallet,
48 setInviteWallet,
49 derivedPoint,
50 setDerivedPoint,
51 } = useActivateFlow();
53 const goToLogin = useCallback(() => history.popAndPush(ROUTE_NAMES.LOGIN), [
54 history,
55 ]);
56 const goToPassport = useCallback(() => {
57 push(names.PASSPORT);
59 if (!hasDisclaimed) {
60 push(names.DISCLAIMER);
61 }
62 }, [names, push, hasDisclaimed]);
64 // validate should be a pure function but we don't want to have to recompute
65 // all of this information on submit, so cache the invite wallet and avoid
66 // re-renders that may trigger re-validations (causing infinite loop)
67 const validateForm = useCallback(
68 async ({ ticket }, errors) => {
69 if (hasErrors(errors)) {
70 return errors;
71 }
73 console.log('FUCK — deriving seed');
75 await timeout(100); // allow the ui changes to flush before we lag it out
77 const _contracts = need.contracts(contracts);
78 const { seed } = await generateTemporaryOwnershipWallet(ticket);
80 // TODO(fang): isn't all this accessible in the ownership object?
81 const inviteWallet = walletFromMnemonic(seed, DEFAULT_HD_PATH);
82 cachedInviteWallet.current = inviteWallet;
84 const _inviteWallet = need.wallet(inviteWallet);
86 const owned = await azimuth.azimuth.getOwnedPoints(
87 _contracts,
88 _inviteWallet.address
89 );
90 const transferring = await azimuth.azimuth.getTransferringFor(
91 _contracts,
92 _inviteWallet.address
93 );
94 const incoming = [...owned, ...transferring];
96 if (incoming.length > 0) {
97 if (incoming.length > 1) {
98 // TODO: warnings
99 // setGeneralError(
100 // 'This invite code has multiple points available.\n' +
101 // "Once you've activated this point, activate the next with the same process."
102 // );
103 }
105 const point = parseInt(incoming[0], 10);
106 setDerivedPoint(Just(point));
107 cachedPoint.current = point;
108 } else {
109 return {
110 [FORM_ERROR]:
111 'Invite code has no claimable point.\n' +
112 'Check your invite code and try again?',
113 };
114 }
115 },
116 [contracts, setDerivedPoint]
117 );
119 const validate = useMemo(
120 () => composeValidator({ ticket: buildTicketValidator() }, validateForm),
121 [validateForm]
122 );
124 // set our state on submission
125 const onSubmit = useCallback(
126 async values => {
127 setInviteWallet(cachedInviteWallet.current);
128 setDerivedWallet(Just(await generateWallet(cachedPoint)));
129 },
130 [setDerivedWallet, setInviteWallet]
131 );
133 const afterSubmit = useCallback(async () => goToPassport(), [goToPassport]);
135 return (
136 <View inset>
137 <Grid>
138 <Grid.Item full as={Passport} point={derivedPoint} />
139 <Grid.Item full as={H4} className="mt3 mb2">
140 Activate
141 </Grid.Item>
142 <BridgeForm
143 validate={validate}
144 onSubmit={onSubmit}
145 afterSubmit={afterSubmit}
146 initialValues={{ ticket: impliedTicket || '' }}>
147 {({ validating, submitting, handleSubmit }) => (
148 <>
149 <Grid.Item
150 full
151 as={TicketInput}
152 name="ticket"
153 label="Activation Code"
154 />
156 <Grid.Item full as={FormError} />
158 <Grid.Item
159 full
160 as={SubmitButton}
161 className="mt4"
162 handleSubmit={handleSubmit}>
163 {validating
164 ? 'Deriving...'
165 : submitting
166 ? 'Generating...'
167 : 'Go'}
168 </Grid.Item>
169 </>
170 )}
171 </BridgeForm>
172 </Grid>
173 <FooterButton as={ForwardButton} onClick={goToLogin}>
174 Login
175 </FooterButton>
176 </View>
177 );