@@ -0,0 +1,204 @@
1+import React, { useCallback, useMemo } from 'react';
2+import * as need from 'lib/need';
3+
4+import ActivateView from './ActivateView';
5+import { Box, Text } from '@tlon/indigo-react';
6+import { composeValidator, buildPatqValidator } from 'form/validators';
7+import { DEFAULT_FADE_TIMEOUT } from 'lib/constants';
8+import { FadeableActivateButton as ActivateButton } from './ActivateButton';
9+import { FadeableActivateHeader as ActivateHeader } from './ActivateHeader';
10+import { FadeableActivateParagraph as ActivateParagraph } from './ActivateParagraph';
11+import { FadeableActivateSteps as ActivateSteps } from './ActivateSteps';
12+import { isDevelopment } from 'lib/flags';
13+import { ticketFromSegments, ticketToSegments } from 'form/formatters';
14+import { timeout } from 'lib/timeout';
15+import { useActivateFlow } from './ActivateFlow';
16+import { useLocalRouter } from 'lib/LocalRouter';
17+import { validateExactly } from 'lib/validators';
18+import BridgeForm from 'form/BridgeForm';
19+import FormError from 'form/FormError';
20+import useFadeIn from './useFadeIn';
21+import {
22+ HiddenInput,
23+ TicketSegmentInput,
24+} from 'form/Inputs';
25+
26+const MasterKeyConfirm = () => {
27+ const { derivedWallet, setIsIn } = useActivateFlow();
28+ const { ticket } = need.wallet(derivedWallet);
29+ const { push, names } = useLocalRouter();
30+ const STUB_VERIFY_TICKET = isDevelopment;
31+ const ticketSegments = useMemo(() => ticketToSegments(ticket), [ticket]);
32+
33+ const onValues = useCallback(({ valid, values, form }) => {
34+ console.log('valid:', valid);
35+ console.log('values:', values);
36+
37+ const { ticket0, ticket1, ticket2, ticket3 } = values;
38+
39+ const updatedTicket = ticketFromSegments([
40+ ticket0,
41+ ticket1,
42+ ticket2,
43+ ticket3,
44+ ]);
45+
46+ console.log(updatedTicket);
47+
48+ form.change('ticket', updatedTicket);
49+ }, []);
50+
51+ const validate = useMemo(
52+ () =>
53+ composeValidator({
54+ ticket: buildPatqValidator([
55+ validateExactly(ticket, 'Does not match expected master ticket.'),
56+ ]),
57+ }),
58+ [ticket]
59+ );
60+
61+ const initialValues = useMemo(
62+ () => ({
63+ ticket: STUB_VERIFY_TICKET ? ticket : undefined,
64+ ticket0: STUB_VERIFY_TICKET ? ticketSegments[0] : undefined,
65+ ticket1: STUB_VERIFY_TICKET ? ticketSegments[1] : undefined,
66+ ticket2: STUB_VERIFY_TICKET ? ticketSegments[2] : undefined,
67+ ticket3: undefined,
68+ }),
69+ [STUB_VERIFY_TICKET, ticket, ticketSegments]
70+ );
71+
72+ const goToTransfer = useCallback(async () => {
73+ setIsIn(false);
74+ await timeout(DEFAULT_FADE_TIMEOUT); // Pause for UI fade animation
75+ push(names.TRANSFER);
76+ }, [names.TRANSFER, push, setIsIn]);
77+
78+ const header = useMemo(() => {
79+ return (
80+ <Box>
81+ <ActivateHeader copy={'Confirm Backup'} />
82+ <ActivateParagraph
83+ copy={
84+ 'Confirm that you have stored your Master Key somewhere safe by entering it below'
85+ }
86+ />
87+ </Box>
88+ );
89+ }, []);
90+
91+ useFadeIn();
92+
93+ return (
94+ <>
95+ <ActivateView
96+ header={header}
97+ gridRows={'20% 80%'}
98+ gridAreas={"'header' 'content'"}>
99+ <Box
100+ alignItems={'center'}
101+ display={'flex'}
102+ flexDirection={'column'}
103+ flexWrap={'nowrap'}
104+ height={'100%'}
105+ justifyContent={'center'}>
106+ <BridgeForm
107+ style={{
108+ display: 'flex',
109+ flexFlow: 'column nowrap',
110+ height: '100%',
111+ width: '100%',
112+ justifyContent: 'center',
113+ alignItems: 'center',
114+ }}
115+ validate={validate}
116+ afterSubmit={goToTransfer}
117+ onValues={onValues}
118+ initialValues={initialValues}>
119+ {({ handleSubmit, valid }) => (
120+ <Box
121+ display={'grid'}
122+ gridTemplateAreas={"'inputs' 'submit'"}
123+ gridTemplateColumns={'1fr'}
124+ gridTemplateRows={'80% 20%'}
125+ width={'100%'}
126+ height={'100%'}>
127+ <Box
128+ gridArea="inputs"
129+ display={'flex'}
130+ flexDirection={'column'}
131+ flexWrap={'nowrap'}
132+ height={'100%'}
133+ alignItems={'center'}
134+ justifyContent={'center'}>
135+ <Box
136+ display={'flex'}
137+ flexDirection={'row'}
138+ width={'80%'}
139+ height={'min-content'}
140+ flexWrap={'nowrap'}
141+ justifyContent={'center'}>
142+ {ticketSegments.map((segment, i) => {
143+ return (
144+ <Box
145+ key={i}
146+ display={'flex'}
147+ flexDirection={'row'}
148+ flexWrap={'nowrap'}>
149+ <TicketSegmentInput
150+ width={'75px'}
151+ paddingY={'18px'}
152+ fontSize={'16px'}
153+ height={'24px'}
154+ fontFamily={'Source Code Pro'}
155+ borderRadius={'5px'}
156+ border={'solid 1px rgba(0,0,0,0.25)'}
157+ name={`ticket${i}`}
158+ />
159+ {i < ticketSegments.length - 1 ? (
160+ <Text
161+ key={i}
162+ alignSelf="center"
163+ color="gray"
164+ mx={1}>
165+ —
166+ </Text>
167+ ) : null}
168+ </Box>
169+ );
170+ })}
171+ </Box>
172+ </Box>
173+
174+ <Box
175+ full
176+ style={{ display: 'none' }}
177+ as={HiddenInput}
178+ name="ticket"
179+ />
180+
181+ <Box as={FormError} />
182+
183+ <Box
184+ display="flex"
185+ flexDirection="column"
186+ flexWrap="nowrap"
187+ height={'100%'}
188+ justifyContent="flex-end"
189+ gridArea="submit">
190+ <ActivateButton disabled={!valid} onClick={handleSubmit}>
191+ Confirm Backup
192+ </ActivateButton>
193+ </Box>
194+ </Box>
195+ )}
196+ </BridgeForm>
197+ </Box>
198+ </ActivateView>
199+ <ActivateSteps currentStep={2} totalSteps={4} overrideFadeIn={true} />
200+ </>
201+ );
202+};
203+
204+export default MasterKeyConfirm;