on fc0bf50 help sign in
bridge/src/views/Activate/ActivateCode.js
171 lines 5.5 kB history raw
1import React, { useCallback, useMemo } from 'react';
2import { Just } 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';
24import useBreakpoints from 'lib/useBreakpoints';
25import WarningBox from 'components/WarningBox';
27import BridgeForm from 'form/BridgeForm';
28import SubmitButton from 'form/SubmitButton';
29import { TicketInput } from 'form/Inputs';
30import { composeValidator, buildPatqValidator } from 'form/validators';
31import FormError from 'form/FormError';
32import { FORM_ERROR } from 'final-form';
34export default function ActivateCode() {
35 const history = useHistory();
36 const { names, push } = useLocalRouter();
37 const { contracts } = useNetwork();
38 const impliedTicket = useImpliedTicket();
39 const [hasDisclaimed] = useHasDisclaimed();
41 const {
42 setDerivedWallet,
43 setInviteWallet,
44 derivedPoint,
45 setDerivedPoint,
46 } = useActivateFlow();
47 // this is a pretty naive way to detect if we're on a mobile device
48 // (i.e. we're checking the width of the screen)
49 // but it will suffice for the 99% case and if someone wants to get around it
50 // well by golly they're allowed to turn their phone into landscape mode
51 // for this screen
52 const activationAllowed = useBreakpoints([false, true, true]);
54 const goToLogin = useCallback(() => history.popAndPush(ROUTE_NAMES.LOGIN), [
55 history,
56 ]);
58 const goToPassport = useCallback(() => {
59 push(names.PASSPORT);
61 if (!hasDisclaimed) {
62 push(names.DISCLAIMER);
63 }
64 }, [names, push, hasDisclaimed]);
66 const validate = useMemo(
67 () => composeValidator({ ticket: buildPatqValidator() }),
68 []
69 );
71 // set our state on submission
72 const onSubmit = useCallback(
73 async values => {
74 await timeout(100); // allow the ui changes to flush before we lag it out
76 const _contracts = need.contracts(contracts);
77 const { seed } = await generateTemporaryOwnershipWallet(values.ticket);
79 const inviteWallet = walletFromMnemonic(seed, DEFAULT_HD_PATH);
81 const _inviteWallet = need.wallet(inviteWallet);
83 const owned = await azimuth.azimuth.getOwnedPoints(
84 _contracts,
85 _inviteWallet.address
86 );
87 const transferring = await azimuth.azimuth.getTransferringFor(
88 _contracts,
89 _inviteWallet.address
90 );
91 const incoming = [...owned, ...transferring];
93 if (incoming.length > 0) {
94 if (incoming.length > 1) {
95 // TODO: putting a warning here doesn't make sense since the user
96 // will be immediately redirected away — what do?
97 // 'This invite code has multiple points available.\n' +
98 // "Once you've activated this point, activate the next with the same process.";
99 }
101 const point = parseInt(incoming[0], 10);
103 setDerivedPoint(Just(point));
104 setInviteWallet(inviteWallet);
105 setDerivedWallet(Just(await generateWallet(point)));
106 } else {
107 return {
108 [FORM_ERROR]:
109 'Invite code has no claimable point.\n' +
110 'Check your invite code and try again?',
111 };
112 }
113 },
114 [contracts, setDerivedPoint, setDerivedWallet, setInviteWallet]
115 );
117 const initialValues = useMemo(() => ({ ticket: impliedTicket || '' }), [
118 impliedTicket,
119 ]);
121 return (
122 <View inset>
123 <Grid>
124 <Grid.Item full as={Passport} point={derivedPoint} />
125 <Grid.Item full as={H4} className="mt3 mb2">
126 Activate
127 </Grid.Item>
128 <BridgeForm
129 validate={validate}
130 onSubmit={onSubmit}
131 afterSubmit={goToPassport}
132 initialValues={initialValues}>
133 {({ validating, submitting, handleSubmit }) => (
134 <>
135 <Grid.Item
136 full
137 as={TicketInput}
138 name="ticket"
139 label="Activation Code"
140 disabled={!activationAllowed}
141 />
143 <Grid.Item full as={FormError} />
145 <Grid.Item
146 full
147 as={SubmitButton}
148 className="mt4"
149 handleSubmit={handleSubmit}>
150 {validating
151 ? 'Deriving...'
152 : submitting
153 ? 'Generating...'
154 : 'Go'}
155 </Grid.Item>
157 {!activationAllowed && (
158 <Grid.Item full as={WarningBox} className="mt4">
159 For your security, please access Bridge on a desktop device.
160 </Grid.Item>
161 )}
162 </>
163 )}
164 </BridgeForm>
165 </Grid>
166 <FooterButton as={ForwardButton} onClick={goToLogin}>
167 Login
168 </FooterButton>
169 </View>
170 );