on bd2bfea help sign in
bridge/src/views/Activate/ActivateCode.js
223 lines 6.9 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, P, CheckboxInput } from 'indigo-react';
5import { FORM_ERROR } from 'final-form';
7import View from 'components/View';
8import { ForwardButton } from 'components/Buttons';
9import Passport from 'components/Passport';
10import WarningBox from 'components/WarningBox';
11import FooterButton from 'components/FooterButton';
13import { useNetwork } from 'store/network';
14import { useHistory } from 'store/history';
16import * as need from 'lib/need';
17import { ROUTE_NAMES } from 'lib/routeNames';
18import { walletFromMnemonic } from 'lib/wallet';
19import { DEFAULT_HD_PATH } from 'lib/constants';
20import { generateWallet } from 'lib/invite';
21import { generateTemporaryOwnershipWallet } from 'lib/walletgen';
22import { useLocalRouter } from 'lib/LocalRouter';
23import useImpliedTicket from 'lib/useImpliedTicket';
24import { timeout } from 'lib/timeout';
25import useHasDisclaimed from 'lib/useHasDisclaimed';
26import useBreakpoints from 'lib/useBreakpoints';
28import BridgeForm from 'form/BridgeForm';
29import SubmitButton from 'form/SubmitButton';
30import { TicketInput } from 'form/Inputs';
31import {
32 composeValidator,
33 buildPatqValidator,
34 hasErrors,
35 buildCheckboxValidator,
36} from 'form/validators';
37import FormError from 'form/FormError';
39import { useActivateFlow } from './ActivateFlow';
40import { WARNING } from 'form/helpers';
41import { convertToInt } from 'lib/convertToInt';
43export default function ActivateCode() {
44 const history = useHistory();
45 const { names, push } = useLocalRouter();
46 const { contracts } = useNetwork();
47 const impliedTicket = useImpliedTicket();
48 const [hasDisclaimed] = useHasDisclaimed();
49 const didWarn = useRef(false);
51 const {
52 setDerivedWallet,
53 setInviteWallet,
54 derivedPoint,
55 setDerivedPoint,
56 } = useActivateFlow();
57 // this is a pretty naive way to detect if we're on a mobile device
58 // (i.e. we're checking the width of the screen)
59 // but it will suffice for the 99% case and if someone wants to get around it
60 // well by golly they're allowed to turn their phone into landscape mode
61 // for this screen
62 const activationAllowed = useBreakpoints([false, true, true]);
64 const goToLogin = useCallback(() => history.popAndPush(ROUTE_NAMES.LOGIN), [
65 history,
66 ]);
68 const goToPassport = useCallback(() => {
69 if (!hasDisclaimed) {
70 push(names.DISCLAIMER, { next: names.PASSPORT });
71 } else {
72 push(names.PASSPORT);
73 }
74 }, [hasDisclaimed, names.DISCLAIMER, names.PASSPORT, push]);
76 const validateForm = useCallback((values, errors) => {
77 didWarn.current = false;
79 if (hasErrors(errors)) {
80 return errors;
81 }
82 }, []);
84 const validate = useMemo(
85 () =>
86 composeValidator(
87 { ticket: buildPatqValidator(), showTicket: buildCheckboxValidator() },
88 validateForm
89 ),
90 [validateForm]
91 );
93 // set our state on submission
94 const onSubmit = useCallback(
95 async values => {
96 await timeout(16); // allow the ui changes to flush before we lag it out
98 const _contracts = need.contracts(contracts);
99 const { seed } = await generateTemporaryOwnershipWallet(values.ticket);
101 const inviteWallet = walletFromMnemonic(seed, DEFAULT_HD_PATH);
103 const _inviteWallet = need.wallet(inviteWallet);
105 const owned = await azimuth.azimuth.getOwnedPoints(
106 _contracts,
107 _inviteWallet.address
108 );
109 const transferring = await azimuth.azimuth.getTransferringFor(
110 _contracts,
111 _inviteWallet.address
112 );
113 const incoming = [...owned, ...transferring];
115 if (incoming.length > 0) {
116 if (incoming.length > 1 && !didWarn.current) {
117 didWarn.current = true;
118 return {
119 [WARNING]:
120 'This invite code has multiple points available. ' +
121 "Once you've activated this point, " +
122 'activate the next with the same process.',
123 };
124 }
126 const point = convertToInt(incoming[0], 10);
128 setDerivedPoint(Just(point));
129 setInviteWallet(inviteWallet);
130 setDerivedWallet(Just(await generateWallet(point, true)));
131 } else {
132 return {
133 [FORM_ERROR]:
134 'Invite code has no claimable point.\n' +
135 'Check your invite code and try again?',
136 };
137 }
138 },
139 [contracts, setDerivedPoint, setDerivedWallet, setInviteWallet]
140 );
142 const initialValues = useMemo(
143 () => ({ ticket: impliedTicket || '', showTicket: true }),
144 [impliedTicket]
145 );
147 return (
148 <View inset>
149 <Grid>
150 <Grid.Item
151 full
152 as={Passport}
153 point={derivedPoint}
154 address={Nothing()}
155 />
156 <Grid.Item full as={H4} className="mt3">
157 Activate
158 </Grid.Item>
159 <Grid.Item full as={P} className="mb2">
160 Someone has invited you to claim your Urbit identity and join the
161 network. {!impliedTicket && 'Enter your activation code to continue.'}
162 </Grid.Item>
163 <BridgeForm
164 validate={validate}
165 onSubmit={onSubmit}
166 afterSubmit={goToPassport}
167 initialValues={initialValues}>
168 {({ validating, values, submitting, handleSubmit }) => (
169 <>
170 {!impliedTicket && (
171 <>
172 <Grid.Item
173 full
174 as={TicketInput}
175 type={values.showTicket ? 'text' : 'password'}
176 name="ticket"
177 label="Activation Code"
178 disabled={!activationAllowed}
179 />
181 <Grid.Item
182 full
183 as={CheckboxInput}
184 name="showTicket"
185 label="Show"
186 />
187 </>
188 )}
190 <Grid.Item full as={FormError} />
192 <Grid.Item
193 full
194 as={SubmitButton}
195 className="mt4"
196 handleSubmit={handleSubmit}>
197 {isWarning =>
198 validating
199 ? 'Deriving...'
200 : submitting
201 ? 'Generating...'
202 : isWarning
203 ? 'Continue Activation'
204 : 'Go'
205 }
206 </Grid.Item>
208 {!activationAllowed && (
209 <Grid.Item full as={WarningBox} className="mt4">
210 For your security, please access Bridge on a desktop device.
211 </Grid.Item>
212 )}
213 </>
214 )}
215 </BridgeForm>
216 </Grid>
218 <FooterButton as={ForwardButton} onClick={goToLogin}>
219 Login
220 </FooterButton>
221 </View>
222 );