@@ -1,78 +1,29 @@
1−import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2−import { Just } from 'folktale/maybe';
3−import * as azimuth from 'azimuth-js';
4−import { Grid, CheckboxInput } from 'indigo-react';
5−import { FORM_ERROR } from 'final-form';
1+import { useCallback, useEffect } from 'react';
62
73import View from 'components/View';
84import { ForwardButton } from 'components/Buttons';
9−import WarningBox from 'components/WarningBox';
105import FooterButton from 'components/FooterButton';
116import ActivateView from './ActivateView';
127
13−import { useNetwork } from 'store/network';
148import { useHistory } from 'store/history';
159
16−import * as need from 'lib/need';
1710import { ROUTE_NAMES } from 'lib/routeNames';
18−import { walletFromMnemonic } from 'lib/wallet';
19−import { DEFAULT_HD_PATH, POINT_DOMINIONS, POINT_PROXIES } from 'lib/constants';
20−import { generateWallet } from 'lib/invite';
21−import { generateTemporaryOwnershipWallet } from 'lib/walletgen';
2211import { useLocalRouter } from 'lib/LocalRouter';
2312import useImpliedTicket from 'lib/useImpliedTicket';
24−import { timeout } from 'lib/timeout';
2513import useHasDisclaimed from 'lib/useHasDisclaimed';
26−import useBreakpoints from 'lib/useBreakpoints';
27−
28−import BridgeForm from 'form/BridgeForm';
29−import SubmitButton from 'form/SubmitButton';
30−import { TicketInput } from 'form/Inputs';
31−import {
32− composeValidator,
33− buildPatqValidator,
34− hasErrors,
35− buildCheckboxValidator,
36−} from 'form/validators';
37−import FormError from 'form/FormError';
3814
3915import { useActivateFlow } from './ActivateFlow';
40−import { WARNING } from 'form/helpers';
41−import { convertToInt } from 'lib/convertToInt';
4216import PointPresenter from './PointPresenter';
43−import useRoller from 'lib/useRoller';
44−import { Ship } from '@urbit/roller-api';
4517import { Box, Text } from '@tlon/indigo-react';
4618import ActivateHeader from './ActivateHeader';
47−import { MasterKey } from './MasterKey';
19+import ActivateCodeForm from './ActivateCodeForm';
4820
4921export default function ActivateCode() {
5022 const history = useHistory();
5123 const { names, push } = useLocalRouter();
52− const { contracts } = useNetwork();
53− const {
54− impliedAzimuthPoint,
55− impliedPatp,
56− impliedTicket,
57− } = useImpliedTicket();
24+ const { impliedPatp, impliedTicket } = useImpliedTicket();
5825 const [hasDisclaimed] = useHasDisclaimed();
59− const didWarn = useRef(false);
60− const { api, getPoints } = useRoller();
61− const [isGenerating, setIsGenerating] = useState<boolean>(false);
62−
63− const {
64− isFaded,
65− setDerivedPoint,
66− setDerivedWallet,
67− setInviteWallet,
68− setIsFaded,
69− } = useActivateFlow();
70− // this is a pretty naive way to detect if we're on a mobile device
71− // (i.e. we're checking the width of the screen)
72− // but it will suffice for the 99% case and if someone wants to get around it
73− // well by golly they're allowed to turn their phone into landscape mode
74− // for this screen
75− const activationAllowed = useBreakpoints([false, true, true]);
26+ const { isIn, setIsIn } = useActivateFlow();
7627
7728 const goToLogin = useCallback(() => history.popAndPush(ROUTE_NAMES.LOGIN), [
7829 history,
@@ -86,215 +37,18 @@ export default function ActivateCode() {
8637 }
8738 }, [hasDisclaimed, names.DISCLAIMER, names.MASTER_KEY, push]);
8839
89− const validateForm = useCallback((values, errors) => {
90− didWarn.current = false;
91−
92− if (hasErrors(errors)) {
93− return errors;
94− }
95− }, []);
96−
97− const validate = useMemo(
98− () =>
99− composeValidator(
100− { ticket: buildPatqValidator(), showTicket: buildCheckboxValidator() },
101− validateForm
102− ),
103− [validateForm]
104− );
105−
106− const loadL1Points = useCallback(
107− async (address: string): Promise<number[]> => {
108− const _contracts = need.contracts(contracts);
109− const owned = await azimuth.azimuth.getOwnedPoints(_contracts, address);
110− const transferring = await azimuth.azimuth.getTransferringFor(
111− _contracts,
112− address
113− );
114− const incoming = [...owned, ...transferring];
115− console.log('incoming L1 points', incoming);
116− return incoming;
117− },
118− [contracts]
119− );
120−
121− const loadl2points = useCallback(
122− async (address: string): Promise<Ship[]> => {
123− const owned = await getPoints(POINT_PROXIES.OWN, address);
124− const transferring = await getPoints(POINT_PROXIES.TRANSFER, address);
125− const incoming = [...owned, ...transferring];
126− console.log('incoming L2 points', incoming);
127− return incoming;
128− },
129− [getPoints]
130− );
131−
132− // derive and set our state on submission
133− const onSubmit = useCallback(
134− async values => {
135− setIsGenerating(true);
136− setIsFaded(true);
137− await timeout(1900); // Allow the Master Key animation to run for one cycle
138−
139− // Derive wallet
140− // TODO: fallback logic to first use implied values if present, otherwise form values?
141− const ticket = impliedTicket || values.ticket;
142− const { seed } = await generateTemporaryOwnershipWallet(ticket);
143− const inviteWallet = walletFromMnemonic(seed, DEFAULT_HD_PATH);
144− setInviteWallet(inviteWallet);
145− const _inviteWallet = need.wallet(inviteWallet);
146− const inviteAddress = _inviteWallet.address;
147−
148− // Query for points depending on point's dominion
149− const rollerPoint = await api.getPoint(impliedAzimuthPoint!);
150− let incoming: number[] | Ship[];
151− if (rollerPoint.dominion === POINT_DOMINIONS.L2) {
152− incoming = await loadl2points(inviteAddress);
153− } else {
154− incoming = await loadL1Points(inviteAddress);
155− }
156−
157− // Set derived
158− if (incoming.length > 0) {
159− if (incoming.length > 1 && !didWarn.current) {
160− didWarn.current = true;
161− setIsGenerating(false);
162−
163− return {
164− [WARNING]:
165− 'This invite code has multiple points available. ' +
166− "Once you've activated this point, " +
167− 'activate the next with the same process.',
168− };
169− }
170−
171− const point = convertToInt(incoming[0], 10);
172−
173− setDerivedPoint(Just(point));
174− setDerivedWallet(Just(await generateWallet(point, true)));
175− } else {
176− setIsGenerating(false);
177−
178− return {
179− [FORM_ERROR]:
180− 'Invite code has no claimable point.\n' +
181− 'Check your invite code and try again?',
182− };
183− }
184− },
185− [
186− api,
187− impliedAzimuthPoint,
188− impliedTicket,
189− loadL1Points,
190− loadl2points,
191− setDerivedPoint,
192− setDerivedWallet,
193− setInviteWallet,
194− setIsFaded,
195− ]
196− );
197−
198− const initialValues = useMemo(
199− () => ({ ticket: impliedTicket || '', showTicket: true }),
200− [impliedTicket]
201− );
202−
40+ // Fade in on load
20341 useEffect(() => {
204− const fetchRollerPoint = async () => {
205− if (!impliedAzimuthPoint) {
206− return;
207− }
208− const p = await api.getPoint(impliedAzimuthPoint!);
209− console.log(p);
210− };
211−
212− fetchRollerPoint();
213− }, [api, impliedAzimuthPoint]);
214−
215− const footer = useMemo(() => {
216− return (
217− <Box
218− display="flex"
219− flexDirection="column"
220− flexWrap="nowrap"
221− height={'100%'}
222− justifyContent="flex-end">
223− <Grid>
224− <BridgeForm
225− validate={validate}
226− onSubmit={onSubmit}
227− afterSubmit={goToMasterKey}
228− initialValues={initialValues}>
229− {({ validating, values, submitting, handleSubmit }) => (
230− <>
231− {!impliedTicket && (
232− <>
233− <Grid.Item
234− full
235− as={TicketInput}
236− type={values.showTicket ? 'text' : 'password'}
237− name="ticket"
238− label="Activation Code"
239− disabled={!activationAllowed}
240− />
241−
242− <Grid.Item
243− full
244− as={CheckboxInput}
245− name="showTicket"
246− label="Show"
247− />
248− </>
249− )}
250−
251− <Grid.Item full as={FormError} />
252−
253− <Grid.Item
254− full
255− as={SubmitButton}
256− className="mt4"
257− handleSubmit={handleSubmit}>
258− {isWarning =>
259− validating
260− ? 'Deriving...'
261− : submitting
262− ? 'Generating...'
263− : isWarning
264− ? 'Continue Activation'
265− : 'Claim'
266− }
267− </Grid.Item>
268−
269− {!activationAllowed && (
270− <Grid.Item full as={WarningBox} className="mt4">
271− For your security, please access Bridge on a desktop device.
272− </Grid.Item>
273− )}
274− </>
275− )}
276− </BridgeForm>
277− </Grid>
278− </Box>
279− );
280− }, [
281− activationAllowed,
282− goToMasterKey,
283− impliedTicket,
284− initialValues,
285− onSubmit,
286− validate,
287− ]);
42+ setIsIn(true);
43+ }, [setIsIn]);
28844
28945 return (
29046 <View inset>
29147 <ActivateView
29248 header={
293− !isGenerating && (
294− <ActivateHeader copy={'Welcome. This is your Urbit.'} />
295− )
49+ <ActivateHeader copy={'Welcome. This is your Urbit.'} isIn={isIn} />
29650 }
297− footer={!isGenerating && footer}>
51+ footer={<ActivateCodeForm afterSubmit={goToMasterKey} />}>
29852 <Box
29953 alignItems={'center'}
30054 display={'flex'}
@@ -305,22 +59,7 @@ export default function ActivateCode() {
30559 {!impliedTicket && (
30660 <Text className="mb2">Enter your activation code to continue.</Text>
30761 )}
308− {!isGenerating && impliedPatp && (
309− <PointPresenter
310− patp={impliedPatp}
311− className={!isFaded ? 'faded-in' : 'faded-out'}
312− />
313− )}
314− <Box
315− display={'flex'}
316− flexDirection={'row'}
317− flexWrap={'nowrap'}
318− className={isFaded ? 'faded-in' : 'faded-out'}
319− width={'80%'}
320− height={'min-content'}
321− justifyContent={'center'}>
322− <MasterKey />
323− </Box>
62+ {impliedPatp && <PointPresenter patp={impliedPatp} isIn={isIn} />}
32463 </Box>
32564 </ActivateView>
32665