on master help sign in
Merge pull request #235 from urbit/feat/ticket-masking
64dc313Matt Condon
src/views/Activate/ActivateCode.js+20−6
@@ -1,7 +1,7 @@
1import React, { useCallback, useMemo, useRef } from 'react';
1import React, { useCallback, useMemo, useRef } from 'react';
2import { Just } from 'folktale/maybe';
2import { Just } from 'folktale/maybe';
3import * as azimuth from 'azimuth-js';
3import * as azimuth from 'azimuth-js';
4−import { Grid, H4 } from 'indigo-react';
4+import { Grid, H4, CheckboxInput } from 'indigo-react';
5import { FORM_ERROR } from 'final-form';
5import { FORM_ERROR } from 'final-form';
6
6
7import View from 'components/View';
7import View from 'components/View';
@@ -31,6 +31,7 @@ import {
31 composeValidator,
31 composeValidator,
32 buildPatqValidator,
32 buildPatqValidator,
33 hasErrors,
33 hasErrors,
34+ buildCheckboxValidator,
34} from 'form/validators';
35} from 'form/validators';
35import FormError from 'form/FormError';
36import FormError from 'form/FormError';
36
37
@@ -79,7 +80,11 @@ export default function ActivateCode() {
79 }, []);
80 }, []);
80
81
81 const validate = useMemo(
82 const validate = useMemo(
83+ () =>
82− () => composeValidator({ ticket: buildPatqValidator() }, validateForm),
84+ composeValidator(
85+ { ticket: buildPatqValidator(), showTicket: buildCheckboxValidator() },
86+ validateForm
87+ ),
83 [validateForm]
88 [validateForm]
84 );
89 );
85
90
@@ -132,9 +137,10 @@ export default function ActivateCode() {
132 [contracts, setDerivedPoint, setDerivedWallet, setInviteWallet]
137 [contracts, setDerivedPoint, setDerivedWallet, setInviteWallet]
133 );
138 );
134
139
135− const initialValues = useMemo(() => ({ ticket: impliedTicket || '' }), [
140+ const initialValues = useMemo(
141+ () => ({ ticket: impliedTicket || '', showTicket: true }),
136− impliedTicket,
142+ [impliedTicket]
137− ]);
143+ );
138
144
139 return (
145 return (
140 <View inset>
146 <View inset>
@@ -148,16 +154,24 @@ export default function ActivateCode() {
148 onSubmit={onSubmit}
154 onSubmit={onSubmit}
149 afterSubmit={goToPassport}
155 afterSubmit={goToPassport}
150 initialValues={initialValues}>
156 initialValues={initialValues}>
151− {({ validating, submitting, handleSubmit }) => (
157+ {({ validating, values, submitting, handleSubmit }) => (
152 <>
158 <>
153 <Grid.Item
159 <Grid.Item
154 full
160 full
155 as={TicketInput}
161 as={TicketInput}
162+ type={values.showTicket ? 'text' : 'password'}
156 name="ticket"
163 name="ticket"
157 label="Activation Code"
164 label="Activation Code"
158 disabled={!activationAllowed}
165 disabled={!activationAllowed}
159 />
166 />
160
167
168+ <Grid.Item
169+ full
170+ as={CheckboxInput}
171+ name="showTicket"
172+ label="Show"
173+ />
174+
161 <Grid.Item full as={FormError} />
175 <Grid.Item full as={FormError} />
162
176
163 <Grid.Item
177 <Grid.Item
src/views/Activate/PassportVerify.js+17−3
@@ -1,5 +1,5 @@
1import React, { useCallback, useMemo } from 'react';
1import React, { useCallback, useMemo } from 'react';
2−import { Grid, P } from 'indigo-react';
2+import { Grid, P, CheckboxInput } from 'indigo-react';
3
3
4import * as need from 'lib/need';
4import * as need from 'lib/need';
5import { useLocalRouter } from 'lib/LocalRouter';
5import { useLocalRouter } from 'lib/LocalRouter';
@@ -8,7 +8,11 @@ import { isDevelopment } from 'lib/flags';
8
8
9import SubmitButton from 'form/SubmitButton';
9import SubmitButton from 'form/SubmitButton';
10import { TicketInput } from 'form/Inputs';
10import { TicketInput } from 'form/Inputs';
11+import {
12+ composeValidator,
11−import { composeValidator, buildPatqValidator } from 'form/validators';
13+ buildPatqValidator,
14+ buildCheckboxValidator,
15+} from 'form/validators';
12import BridgeForm from 'form/BridgeForm';
16import BridgeForm from 'form/BridgeForm';
13
17
14import { useActivateFlow } from './ActivateFlow';
18import { useActivateFlow } from './ActivateFlow';
@@ -29,6 +33,7 @@ export default function PassportVerify({ className }) {
29 ticket: buildPatqValidator([
33 ticket: buildPatqValidator([
30 validateExactly(ticket, 'Does not match expected master ticket.'),
34 validateExactly(ticket, 'Does not match expected master ticket.'),
31 ]),
35 ]),
36+ showTicket: buildCheckboxValidator(),
32 }),
37 }),
33 [ticket]
38 [ticket]
34 );
39 );
@@ -36,6 +41,7 @@ export default function PassportVerify({ className }) {
36 const initialValues = useMemo(
41 const initialValues = useMemo(
37 () => ({
42 () => ({
38 ticket: STUB_VERIFY_TICKET ? ticket : undefined,
43 ticket: STUB_VERIFY_TICKET ? ticket : undefined,
44+ showTicket: true,
39 }),
45 }),
40 [ticket]
46 [ticket]
41 );
47 );
@@ -52,15 +58,23 @@ export default function PassportVerify({ className }) {
52 validate={validate}
58 validate={validate}
53 afterSubmit={goToTransfer}
59 afterSubmit={goToTransfer}
54 initialValues={initialValues}>
60 initialValues={initialValues}>
55− {({ handleSubmit }) => (
61+ {({ values, handleSubmit }) => (
56 <>
62 <>
57 <Grid.Item
63 <Grid.Item
58 full
64 full
59 as={TicketInput}
65 as={TicketInput}
66+ type={values.showTicket ? 'text' : 'password'}
60 name="ticket"
67 name="ticket"
61 label="Master Ticket"
68 label="Master Ticket"
62 />
69 />
63
70
71+ <Grid.Item
72+ full
73+ as={CheckboxInput}
74+ name="showTicket"
75+ label="Show"
76+ />
77+
64 <Grid.Item full as={FormError} />
78 <Grid.Item full as={FormError} />
65
79
66 <Grid.Item
80 <Grid.Item
src/views/Admin/Reticket/ReticketConfirm.js+3−2
@@ -65,12 +65,12 @@ export default function ReticketConfirm({ newWallet, setNewWallet }) {
65 });
65 });
66
66
67 return (
67 return (
68− <Grid gap={4} className="mt4">
68+ <Grid className="mt4">
69 <Grid.Item full as={Text}>
69 <Grid.Item full as={Text}>
70 Reticketing is the process of generating a completely fresh wallet and
70 Reticketing is the process of generating a completely fresh wallet and
71 transferring ownership of your point to that wallet.
71 transferring ownership of your point to that wallet.
72 </Grid.Item>
72 </Grid.Item>
73− <Grid.Item full as={WarningBox}>
73+ <Grid.Item full as={WarningBox} className="mt4">
74 Beware, this resets your proxy addresses; if you're using smart
74 Beware, this resets your proxy addresses; if you're using smart
75 contracts, this might break them! It will also change your networking
75 contracts, this might break them! It will also change your networking
76 keys!
76 keys!
@@ -79,6 +79,7 @@ export default function ReticketConfirm({ newWallet, setNewWallet }) {
79 full
79 full
80 as={ForwardButton}
80 as={ForwardButton}
81 solid
81 solid
82+ className="mt4"
82 accessory={blinkIf(Nothing.hasInstance(newWallet))}
83 accessory={blinkIf(Nothing.hasInstance(newWallet))}
83 disabled={Nothing.hasInstance(newWallet)}
84 disabled={Nothing.hasInstance(newWallet)}
84 onClick={goDownload}>
85 onClick={goDownload}>
src/views/Admin/Reticket/ReticketDownload.js+2−1
@@ -20,13 +20,14 @@ export default function ReticketDownload({ newWallet }) {
20 const goVerify = useCallback(() => push(names.VERIFY), [push, names]);
20 const goVerify = useCallback(() => push(names.VERIFY), [push, names]);
21
21
22 return (
22 return (
23− <Grid gap={4} className="mt4">
23+ <Grid className="mt4">
24 <Grid.Item full as={Text}>
24 <Grid.Item full as={Text}>
25 Download the thing, and keep it somewhere safe!
25 Download the thing, and keep it somewhere safe!
26 </Grid.Item>
26 </Grid.Item>
27 <Grid.Item
27 <Grid.Item
28 full
28 full
29 as={!downloaded ? DownloadButton : ForwardButton}
29 as={!downloaded ? DownloadButton : ForwardButton}
30+ className="mt4"
30 disabled={Nothing.hasInstance(newWallet)}
31 disabled={Nothing.hasInstance(newWallet)}
31 onClick={!downloaded ? download : goVerify}
32 onClick={!downloaded ? download : goVerify}
32 success={downloaded}
33 success={downloaded}
src/views/Admin/Reticket/ReticketExecute.js+11−12
@@ -110,12 +110,12 @@ export default function ReticketExecute({ newWallet, setNewWallet }) {
110 if (generalError) {
110 if (generalError) {
111 return (
111 return (
112 <>
112 <>
113− <Grid.Item full>
113+ <Grid.Item full className="mt4">
114 <ErrorText>{generalError.message.toString()}</ErrorText>
114 <ErrorText>{generalError.message.toString()}</ErrorText>
115 </Grid.Item>
115 </Grid.Item>
116 <Grid.Item
116 <Grid.Item
117 full
117 full
118− className="mt3"
118+ className="mt4"
119 as={RestartButton}
119 as={RestartButton}
120 solid
120 solid
121 onClick={goToRestart}>
121 onClick={goToRestart}>
@@ -127,7 +127,7 @@ export default function ReticketExecute({ newWallet, setNewWallet }) {
127
127
128 if (needFunds) {
128 if (needFunds) {
129 return (
129 return (
130− <Grid.Item full>
130+ <Grid.Item full className="mt4">
131 <Highlighted warning>
131 <Highlighted warning>
132 The address {needFunds.address} needs at least{' '}
132 The address {needFunds.address} needs at least{' '}
133 {fromWei(needFunds.minBalance)} ETH and currently has{' '}
133 {fromWei(needFunds.minBalance)} ETH and currently has{' '}
@@ -139,21 +139,21 @@ export default function ReticketExecute({ newWallet, setNewWallet }) {
139 }
139 }
140
140
141 return (
141 return (
142− <Grid.Item full as={WarningBox}>
142+ <Grid.Item full as={WarningBox} className="mt4">
143 Never give your Master Ticket to anyone
143 Never give your Master Ticket to anyone
144 </Grid.Item>
144 </Grid.Item>
145 );
145 );
146 };
146 };
147
147
148 return (
148 return (
149− <Grid gap={4} className="mt4">
149+ <Grid className="mt4">
150 <Grid.Item full as={H4}>
150 <Grid.Item full as={H4}>
151 {labelForProgress(progress)}
151 {labelForProgress(progress)}
152 </Grid.Item>
152 </Grid.Item>
153
153
154 {isDone ? (
154 {isDone ? (
155 <>
155 <>
156− <Grid.Item full as={Text}>
156+ <Grid.Item full as={Text} className="mt4">
157 Your changes are now reflected on-chain and you can use the new
157 Your changes are now reflected on-chain and you can use the new
158 ticket to manage your point.
158 ticket to manage your point.
159 </Grid.Item>
159 </Grid.Item>
@@ -161,6 +161,7 @@ export default function ReticketExecute({ newWallet, setNewWallet }) {
161 full
161 full
162 as={ForwardButton}
162 as={ForwardButton}
163 solid
163 solid
164+ className="mt4"
164 accessory="↺"
165 accessory="↺"
165 onClick={loginAndGoHome}>
166 onClick={loginAndGoHome}>
166 Login with New Master Ticket
167 Login with New Master Ticket
@@ -168,13 +169,11 @@ export default function ReticketExecute({ newWallet, setNewWallet }) {
168 </>
169 </>
169 ) : (
170 ) : (
170 <>
171 <>
171− <Grid.Item full as={Grid} className="mt3" gap={3}>
172+ <Grid.Item full as={Grid} className="mt4" gap={3}>
172 <Grid.Item full as={LoadingBar} progress={progress} />
173 <Grid.Item full as={LoadingBar} progress={progress} />
173− <Grid.Item full>
174− <Text className="f5 green4">
174+ <Grid.Item full as={Text} className="f5 green4">
175− This process can take up to 5 minutes to complete. Don't close
175+ This process can take up to 5 minutes to complete. Don't close
176− this page until the process is complete.
176+ this page until the process is complete.
177− </Text>
178 </Grid.Item>
177 </Grid.Item>
179 </Grid.Item>
178 </Grid.Item>
180
179
src/views/Admin/Reticket/ReticketVerify.js+7−3
@@ -1,5 +1,5 @@
1import React, { useCallback, useMemo } from 'react';
1import React, { useCallback, useMemo } from 'react';
2−import { Text, Grid } from 'indigo-react';
2+import { Text, Grid, CheckboxInput } from 'indigo-react';
3
3
4import { useLocalRouter } from 'lib/LocalRouter';
4import { useLocalRouter } from 'lib/LocalRouter';
5import { validateExactly } from 'lib/validators';
5import { validateExactly } from 'lib/validators';
@@ -37,7 +37,7 @@ export default function ReticketVerify({ newWallet }) {
37 const goExecute = useCallback(() => push(names.EXECUTE), [push, names]);
37 const goExecute = useCallback(() => push(names.EXECUTE), [push, names]);
38
38
39 return (
39 return (
40− <Grid gap={4} className="mt4">
40+ <Grid className="mt4">
41 <Grid.Item full as={Text}>
41 <Grid.Item full as={Text}>
42 Verify New Master Ticket
42 Verify New Master Ticket
43 </Grid.Item>
43 </Grid.Item>
@@ -45,15 +45,19 @@ export default function ReticketVerify({ newWallet }) {
45 validate={validate}
45 validate={validate}
46 afterSubmit={goExecute}
46 afterSubmit={goExecute}
47 initialValues={initialValues}>
47 initialValues={initialValues}>
48− {({ handleSubmit }) => (
48+ {({ handleSubmit, values }) => (
49 <>
49 <>
50 <Grid.Item
50 <Grid.Item
51 full
51 full
52 as={TicketInput}
52 as={TicketInput}
53+ className="mt4"
54+ type={values.showTicket ? 'text' : 'password'}
53 name="ticket"
55 name="ticket"
54 label="New master ticket"
56 label="New master ticket"
55 />
57 />
56
58
59+ <Grid.Item full as={CheckboxInput} name="showTicket" label="Show" />
60+
57 <Grid.Item full as={FormError} />
61 <Grid.Item full as={FormError} />
58
62
59 <Grid.Item full as={SubmitButton} handleSubmit={handleSubmit}>
63 <Grid.Item full as={SubmitButton} handleSubmit={handleSubmit}>
src/views/Login/Ticket.js+53−18
@@ -3,7 +3,7 @@ import { Just } from 'folktale/maybe';
3import cn from 'classnames';
3import cn from 'classnames';
4import * as azimuth from 'azimuth-js';
4import * as azimuth from 'azimuth-js';
5import * as kg from 'urbit-key-generation/dist/index';
5import * as kg from 'urbit-key-generation/dist/index';
6−import { Grid, CheckboxInput } from 'indigo-react';
6+import { Grid, CheckboxInput, Flex, ToggleInput } from 'indigo-react';
7import { FORM_ERROR } from 'final-form';
7import { FORM_ERROR } from 'final-form';
8
8
9import { useNetwork } from 'store/network';
9import { useNetwork } from 'store/network';
@@ -116,6 +116,7 @@ export default function Ticket({ className, goHome }) {
116 () =>
116 () =>
117 composeValidator(
117 composeValidator(
118 {
118 {
119+ useAdvanced: buildCheckboxValidator(),
119 usePassphrase: buildCheckboxValidator(),
120 usePassphrase: buildCheckboxValidator(),
120 useShards: buildCheckboxValidator(),
121 useShards: buildCheckboxValidator(),
121 point: buildPointValidator(4),
122 point: buildPointValidator(4),
@@ -123,6 +124,7 @@ export default function Ticket({ className, goHome }) {
123 shard1: buildPatqValidator(),
124 shard1: buildPatqValidator(),
124 shard2: buildPatqValidator(),
125 shard2: buildPatqValidator(),
125 shard3: buildPatqValidator(),
126 shard3: buildPatqValidator(),
127+ showTicket: buildCheckboxValidator(),
126 passphrase: buildPassphraseValidator(),
128 passphrase: buildPassphraseValidator(),
127 },
129 },
128 validateForm
130 validateForm
@@ -135,6 +137,8 @@ export default function Ticket({ className, goHome }) {
135 point: impliedPoint || '',
137 point: impliedPoint || '',
136 usePasshrase: false,
138 usePasshrase: false,
137 useShards: false,
139 useShards: false,
140+ showTicket: true,
141+ useAdvanced: false,
138 }),
142 }),
139 [impliedPoint]
143 [impliedPoint]
140 );
144 );
@@ -146,7 +150,7 @@ export default function Ticket({ className, goHome }) {
146 onSubmit={onSubmit}
150 onSubmit={onSubmit}
147 afterSubmit={goHome}
151 afterSubmit={goHome}
148 initialValues={initialValues}>
152 initialValues={initialValues}>
149− {({ handleSubmit, submitting }) => (
153+ {({ handleSubmit, values, submitting }) => (
150 <>
154 <>
151 <Grid.Item full as={PointInput} name="point" />
155 <Grid.Item full as={PointInput} name="point" />
152
156
@@ -154,17 +158,38 @@ export default function Ticket({ className, goHome }) {
154 <Grid.Item
158 <Grid.Item
155 full
159 full
156 as={TicketInput}
160 as={TicketInput}
161+ type={values.showTicket ? 'text' : 'password'}
157 name="ticket"
162 name="ticket"
158 label="Master Ticket"
163 label="Master Ticket"
159 />
164 />
160 </Condition>
165 </Condition>
161
166
162 <Condition when="useShards" is={true}>
167 <Condition when="useShards" is={true}>
168+ <Grid.Item
169+ full
163− <Grid.Item full as={TicketInput} name="shard1" label="Shard 1" />
170+ as={TicketInput}
171+ type={values.showTicket ? 'text' : 'password'}
172+ name="shard1"
173+ label="Shard 1"
174+ />
175+ <Grid.Item
176+ full
164− <Grid.Item full as={TicketInput} name="shard2" label="Shard 2" />
177+ as={TicketInput}
178+ type={values.showTicket ? 'text' : 'password'}
179+ name="shard2"
180+ label="Shard 2"
181+ />
182+ <Grid.Item
183+ full
165− <Grid.Item full as={TicketInput} name="shard3" label="Shard 3" />
184+ as={TicketInput}
185+ type={values.showTicket ? 'text' : 'password'}
186+ name="shard3"
187+ label="Shard 3"
188+ />
166 </Condition>
189 </Condition>
167
190
191+ <Grid.Item full as={CheckboxInput} name="showTicket" label="Show" />
192+
168 <Condition when="usePassphrase" is={true}>
193 <Condition when="usePassphrase" is={true}>
169 <Grid.Item
194 <Grid.Item
170 full
195 full
@@ -174,19 +199,6 @@ export default function Ticket({ className, goHome }) {
174 />
199 />
175 </Condition>
200 </Condition>
176
201
177− <Grid.Item
178− full
179− as={CheckboxInput}
180− name="usePassphrase"
181− label="Passphrase"
182− />
183− <Grid.Item
184− full
185− as={CheckboxInput}
186− name="useShards"
187− label="Shards"
188− />
189−
190 <Grid.Item full as={FormError} />
202 <Grid.Item full as={FormError} />
191
203
192 <Grid.Item full as={SubmitButton} handleSubmit={handleSubmit}>
204 <Grid.Item full as={SubmitButton} handleSubmit={handleSubmit}>
@@ -198,6 +210,29 @@ export default function Ticket({ className, goHome }) {
198 : 'Continue'
210 : 'Continue'
199 }
211 }
200 </Grid.Item>
212 </Grid.Item>
213+
214+ <Grid.Item full as={Flex} justify="between">
215+ <Flex.Item
216+ as={ToggleInput}
217+ name="useAdvanced"
218+ label="Advanced"
219+ inverseLabel="Hide"
220+ />
221+ <Condition when="useAdvanced" is={true}>
222+ <Flex.Item as={Flex}>
223+ <Flex.Item
224+ as={CheckboxInput}
225+ name="usePassphrase"
226+ label="Passphrase"
227+ />
228+ <Flex.Item
229+ as={CheckboxInput}
230+ name="useShards"
231+ label="Shards"
232+ />
233+ </Flex.Item>
234+ </Condition>
235+ </Grid.Item>
201 </>
236 </>
202 )}
237 )}
203 </BridgeForm>
238 </BridgeForm>