on master help sign in
activate-ux: pasting Ticket auto-populates fields
36aafeetomholford
src/form/Inputs.js → src/form/Inputs.tsxrenamed+37−4
@@ -1,6 +1,7 @@
1import React from 'react';
1import React from 'react';
2+import * as ob from 'urbit-ob';
2import { Input, AccessoryIcon } from 'indigo-react';
3import { Input, AccessoryIcon } from 'indigo-react';
3−import { useField } from 'react-final-form';
4+import { useField, useForm } from 'react-final-form';
4
5
5import {
6import {
6 convertToNumber,
7 convertToNumber,
@@ -9,10 +10,14 @@ import {
9 ensurePatFormat,
10 ensurePatFormat,
10 stripHexPrefix,
11 stripHexPrefix,
11 ensureHexPrefix,
12 ensureHexPrefix,
13+ ticketToSegments,
12} from 'form/formatters';
14} from 'form/formatters';
13import { DEFAULT_HD_PATH } from 'lib/constants';
15import { DEFAULT_HD_PATH } from 'lib/constants';
14import InputSigil from 'components/InputSigil';
16import InputSigil from 'components/InputSigil';
17+import {
18+ StatelessTextInput,
19+ StatelessTextInputProps,
15−import { StatelessTextInput } from '@tlon/indigo-react';
20+} from '@tlon/indigo-react';
16
21
17const PLACEHOLDER_POINT = '~sampel-ponnym';
22const PLACEHOLDER_POINT = '~sampel-ponnym';
18const PLACEHOLDER_HD_PATH = DEFAULT_HD_PATH;
23const PLACEHOLDER_HD_PATH = DEFAULT_HD_PATH;
@@ -210,16 +215,44 @@ export function EmailInput({ ...rest }) {
210 );
215 );
211}
216}
212
217
218+type TicketSegmentInputProps = {
219+ name: string;
220+ rest: StatelessTextInputProps;
221+};
222+
213−export function TicketSegmentInput({ name, ...rest }) {
223+export function TicketSegmentInput({ name, ...rest }: TicketSegmentInputProps) {
214 const { input } = useField(name);
224 const { input } = useField(name);
225+ const { change } = useForm();
226+
227+ const onPaste = (event: React.ClipboardEvent<HTMLInputElement>) => {
228+ try {
229+ const pasteBuffer = event.clipboardData.getData('text');
230+ const patp = ensurePatFormat(pasteBuffer);
231+ if (!ob.isValidPatp(patp)) {
232+ throw new Error('invalid patp');
233+ }
234+
235+ // prevent pasting the entire clipboard text into the field
236+ event.preventDefault();
237+
238+ const segments = ticketToSegments(pasteBuffer);
239+ change('ticket0', segments[0]);
240+ change('ticket1', segments[1]);
241+ change('ticket2', segments[2]);
242+ change('ticket3', segments[3]);
243+ } catch (error) {
244+ console.log(`skipping ticket autofill: ${error}`);
245+ }
246+ };
215
247
216 return (
248 return (
217 <StatelessTextInput
249 <StatelessTextInput
250+ {...input}
218 autoCapitalize="none"
251 autoCapitalize="none"
219 autoCorrect="off"
252 autoCorrect="off"
220 type="text"
253 type="text"
221 maxLength="6"
254 maxLength="6"
222− {...input}
255+ onPaste={onPaste}
223 {...rest}
256 {...rest}
224 />
257 />
225 );
258 );
src/views/Activate/MasterKeyConfirm.tsx+1
@@ -162,6 +162,7 @@ const MasterKeyConfirm = () => {
162 borderRadius={'5px'}
162 borderRadius={'5px'}
163 border={'solid 1px rgba(0,0,0,0.25)'}
163 border={'solid 1px rgba(0,0,0,0.25)'}
164 name={`ticket${i}`}
164 name={`ticket${i}`}
165+ autoFocus={i === 0}
165 />
166 />
166 {i < ticketSegments.length - 1 ? (
167 {i < ticketSegments.length - 1 ? (
167 <Text
168 <Text
src/views/Activate/MasterKeyDownload.tsx+1−1
@@ -32,7 +32,7 @@ const MasterKeyDownload = () => {
32 const { data, push, names } = useLocalRouter();
32 const { data, push, names } = useLocalRouter();
33 const point = need.point(derivedPoint);
33 const point = need.point(derivedPoint);
34 const wallet = need.wallet(derivedWallet);
34 const wallet = need.wallet(derivedWallet);
35− const ticket = wallet.ticket;
35+ const ticket = wallet.ticket.replace('~', '');
36
36
37 const [paper, setPaper] = useState(Nothing());
37 const [paper, setPaper] = useState(Nothing());
38 const [triggerAnimation, setTriggerAnimation] = useState<boolean>(false);
38 const [triggerAnimation, setTriggerAnimation] = useState<boolean>(false);