on 4df90df help sign in
bridge/src/views/Activate/ActivateCode.js
179 lines 5.7 kB history raw
1import React, { useCallback, useEffect, useState } from 'react';
2import { Just, Nothing } from 'folktale/maybe';
3import * as azimuth from 'azimuth-js';
4import { Grid, Input, H4, ErrorText } from 'indigo-react';
6import View from 'components/View';
7import { ForwardButton } from 'components/Buttons';
8import Passport from 'components/Passport';
10import { useHistory } from 'store/history';
12import { useTicketInput } from 'lib/useInputs';
13import * as need from 'lib/need';
14import { ROUTE_NAMES } from 'lib/routeNames';
15import { useSyncKnownPoints } from 'lib/useSyncPoints';
16import FooterButton from 'components/FooterButton';
17import { blinkIf } from 'components/Blinky';
18import { DEFAULT_HD_PATH, walletFromMnemonic } from 'lib/wallet';
19import { useNetwork } from 'store/network';
20import { generateWallet } from 'lib/invite';
21import { generateTemporaryOwnershipWallet } from 'lib/walletgen';
22import { useActivateFlow } from './ActivateFlow';
23import { useLocalRouter } from 'lib/LocalRouter';
24import useImpliedTicket from 'lib/useImpliedTicket';
25import useHasDisclaimed from 'lib/useHasDisclaimed';
26import useBreakpoints from 'lib/useBreakpoints';
27import WarningBox from 'components/WarningBox';
29export default function ActivateCode() {
30 const history = useHistory();
31 const { names, push } = useLocalRouter();
32 const { contracts } = useNetwork();
33 const impliedTicket = useImpliedTicket();
34 const [hasDisclaimed] = useHasDisclaimed();
35 const [generalError, setGeneralError] = useState();
36 const [deriving, setDeriving] = useState(false);
37 const {
38 derivedWallet,
39 setDerivedWallet,
40 setInviteWallet,
41 derivedPoint,
42 setDerivedPoint,
43 } = useActivateFlow();
44 // this is a pretty naive way to detect if we're on a mobile device
45 // (i.e. we're checking the width of the screen)
46 // but it will suffice for the 99% case and if someone wants to get around it
47 // well by golly they're allowed to turn their phone into landscape mode
48 // for this screen
49 const activationAllowed = useBreakpoints([false, true, true]);
51 const [ticketInput, { pass: validTicket, data: ticket }] = useTicketInput({
52 name: 'ticket',
53 label: 'Activation Code',
54 initialValue: impliedTicket || '',
55 autoFocus: true,
56 disabled: !activationAllowed,
57 });
59 const goToLogin = useCallback(() => history.popAndPush(ROUTE_NAMES.LOGIN), [
60 history,
61 ]);
62 const goToPassport = useCallback(() => {
63 push(names.PASSPORT);
64 if (!hasDisclaimed) {
65 push(names.DISCLAIMER);
66 }
67 }, [names, push, hasDisclaimed]);
69 const pass = derivedWallet.matchWith({
70 Nothing: () => false,
71 Just: () => true,
72 });
74 useEffect(() => {
75 if (validTicket) {
76 const _contracts = need.contracts(contracts);
78 setDeriving(true);
79 // when the ticket becomes valid, derive the point
80 (async () => {
81 const { seed } = await generateTemporaryOwnershipWallet(ticket);
83 //TODO isn't all this accessible in the ownership object?
84 const inviteWallet = walletFromMnemonic(seed, DEFAULT_HD_PATH);
85 const _inviteWallet = need.wallet(inviteWallet);
87 const owned = await azimuth.azimuth.getOwnedPoints(
88 _contracts,
89 _inviteWallet.address
90 );
91 const transferring = await azimuth.azimuth.getTransferringFor(
92 _contracts,
93 _inviteWallet.address
94 );
95 const incoming = [...owned, ...transferring];
97 let realPoint = Nothing();
98 let wallet = Nothing();
100 if (incoming.length > 0) {
101 const pointNum = parseInt(incoming[0], 10);
102 realPoint = Just(pointNum);
103 wallet = Just(await generateWallet(pointNum));
105 if (incoming.length > 1) {
106 setGeneralError(
107 'This invite code has multiple points available.\n' +
108 "Once you've activated this point, activate the next with the same process."
109 );
110 } else {
111 setGeneralError(false);
112 }
113 } else {
114 setGeneralError(
115 'Invite code has no claimable point.\n' +
116 'Check your invite code and try again?'
117 );
118 }
120 setDerivedPoint(realPoint);
121 setDerivedWallet(wallet);
122 setInviteWallet(inviteWallet);
123 setDeriving(false);
124 })();
125 } else {
126 setGeneralError(false);
127 }
128 }, [
129 validTicket,
130 contracts,
131 ticket,
132 setDerivedPoint,
133 setDerivedWallet,
134 setInviteWallet,
135 ]);
137 // when we know the derived point, ensure we have the data to display it
138 useSyncKnownPoints([derivedPoint.getOrElse(null)].filter(p => p !== null));
140 return (
141 <View inset>
142 <Grid>
143 <Grid.Item as={Passport} point={derivedPoint} full />
144 <Grid.Item as={H4} className="mt3 mb2" full>
145 Activate
146 </Grid.Item>
148 <Grid.Item as={Input} {...ticketInput} full />
150 {generalError && (
151 <Grid.Item full>
152 <ErrorText>{generalError}</ErrorText>
153 </Grid.Item>
154 )}
156 <Grid.Item
157 as={ForwardButton}
158 className="mt4"
159 disabled={!pass || deriving || !activationAllowed}
160 accessory={blinkIf(deriving)}
161 onClick={goToPassport}
162 solid
163 full>
164 {deriving && 'Deriving...'}
165 {!deriving && 'Go'}
166 </Grid.Item>
168 {!activationAllowed && (
169 <Grid.Item full as={WarningBox} className="mt4">
170 For your security, please access Bridge on a desktop device.
171 </Grid.Item>
172 )}
173 </Grid>
174 <FooterButton as={ForwardButton} onClick={goToLogin}>
175 Login
176 </FooterButton>
177 </View>
178 );