on ef914ee help sign in
bridge/src/lib/useInputs.js
273 lines 6.2 kB history raw
1import React, { useCallback, useMemo } from 'react';
2import { AccessoryIcon, useForm } from 'indigo-react';
3import { identity } from 'lodash';
4import * as bip39 from 'bip39';
6import { DEFAULT_HD_PATH } from 'lib/wallet';
7import {
8 validateNotEmpty,
9 validateMnemonic,
10 validatePoint,
11 validateTicket,
12 validateEmail,
13 validateLength,
14 validateHexString,
15 validateOneOf,
16 validateMaximumPatpByteLength,
17 validateEthereumAddress,
18 validateNotNullAddress,
19 validateGreaterThan,
20} from 'lib/validators';
21import { prependSig, convertToNumber } from 'lib/transformers';
23import InputSigil from 'components/InputSigil';
25const EXAMPLE_PRIVATE_KEY =
26 '0xa44de2416ee6beb2f323fab48b432925c9785808d33a6ca6d7ba00b45e9370c3';
28// pulls out the first input from a useForm() call
29function useFirstOf({ inputs, setValue, ...rest }, mapper = identity) {
30 // ask the mapper function for any values to overwrite
31 const input = {
32 ...inputs[0],
33 ...mapper(inputs[0]),
34 };
35 // memoize the setValue callback for manually setting the value
36 // in response to some imperative event
37 const _setValue = useCallback(value => setValue(input.name, value), [
38 setValue,
39 input.name,
40 ]);
42 // provide the first input as the first element
43 // _and_ as the second element to facilitate destructuring
44 // and then provide the pass/error/setValue/etc properties at the end
45 return [
46 input,
47 input,
48 {
49 ...rest,
50 setValue: _setValue,
51 },
52 ];
55export function usePassphraseInput(props) {
56 return useFirstOf(
57 useForm([
58 {
59 type: 'password',
60 autoComplete: 'off',
61 placeholder: 'Passphrase',
62 ...props,
63 },
64 ])
65 );
68export function useHexInput({ length, ...rest }) {
69 return useFirstOf(
70 useForm([
71 {
72 type: 'text', // or password
73 autoComplete: 'off',
74 placeholder: `e.g. ${EXAMPLE_PRIVATE_KEY}`,
75 validators: useMemo(
76 () => [
77 validateHexString,
78 validateLength(length + 2),
79 validateNotEmpty,
80 ],
81 [length]
82 ),
83 ...rest,
84 },
85 ])
86 );
89const kExampleMnemonic = bip39.generateMnemonic();
90const kMnemonicValidators = [validateMnemonic, validateNotEmpty];
91export function useMnemonicInput(props) {
92 return useFirstOf(
93 useForm([
94 {
95 type: 'text',
96 autoComplete: 'off',
97 placeholder: `e.g. ${kExampleMnemonic}`,
98 validators: kMnemonicValidators,
99 ...props,
100 },
101 ])
102 );
105export function useHdPathInput(props) {
106 return useFirstOf(
107 useForm([
108 {
109 type: 'text',
110 autoComplete: 'off',
111 placeholder: `e.g. ${DEFAULT_HD_PATH}`,
112 ...props,
113 },
114 ])
115 );
118const kTicketValidators = [validateTicket, validateNotEmpty];
119//TODO needs to be fancier, displaying sig and dashes instead of •ing all
120const kTicketTransformers = [prependSig];
121export function useTicketInput({ validators = [], deriving = false, ...rest }) {
122 return useFirstOf(
123 useForm([
124 {
125 type: 'password',
126 label: 'Ticket',
127 placeholder: 'e.g. ~ragtyd-modwen-bostec-hinsep',
128 validators: useMemo(() => [...validators, ...kTicketValidators], [
129 validators,
130 ]),
131 transformers: kTicketTransformers,
132 mono: true,
133 ...rest,
134 },
135 ]),
136 ({ error, pass }) => ({
137 accessory: error ? (
138 <AccessoryIcon.Failure />
139 ) : deriving ? (
140 <AccessoryIcon.Pending />
141 ) : pass ? (
142 <AccessoryIcon.Success />
143 ) : null,
144 })
145 );
148const kPointTransformers = [prependSig];
149export function usePointInput({ size = 4, ...rest }) {
150 const validators = useMemo(
151 () => [
152 validatePoint,
153 validateMaximumPatpByteLength(size),
154 validateNotEmpty,
155 ],
156 [size]
157 );
159 return useFirstOf(
160 useForm([
161 {
162 type: 'text',
163 label: 'Point',
164 placeholder: 'e.g. ~zod',
165 validators,
166 transformers: kPointTransformers,
167 mono: true,
168 ...rest,
169 },
170 ]),
171 ({ error, pass, focused, value }) => ({
172 accessory: value ? (
173 <InputSigil
174 patp={value}
175 size={44}
176 margin={8}
177 pass={pass}
178 focused={focused}
179 error={error}
180 />
181 ) : null,
182 })
183 );
186export function useGalaxyInput(props) {
187 return usePointInput({
188 name: 'galaxy',
189 label: 'Galaxy Name',
190 size: 1,
191 ...props,
192 });
195export function useCheckboxInput({ initialValue, ...rest }) {
196 return useFirstOf(
197 useForm([
198 {
199 type: 'checkbox',
200 ...rest,
201 },
202 ])
203 );
206export function useSelectInput({ initialValue, options, ...rest }) {
207 return useFirstOf(
208 useForm([
209 {
210 type: 'select',
211 validators: useMemo(
212 () => [
213 validateOneOf(
214 options.map(option => option.value),
215 validateNotEmpty
216 ),
217 ],
218 [options]
219 ),
220 options,
221 initialValue: initialValue || options[0].value,
222 ...rest,
223 },
224 ])
225 );
228const kAddressValidators = [
229 validateEthereumAddress,
230 validateNotNullAddress,
231 validateNotEmpty,
232];
233export function useAddressInput({ ...rest }) {
234 return useFirstOf(
235 useForm([
236 {
237 type: 'string',
238 label: 'Ethereum Address',
239 placeholder: 'e.g. 0x6DEfFb0caFDB11D175F123F6891AA64F01c24F7d',
240 autoComplete: 'off',
241 validators: kAddressValidators,
242 mono: true,
243 ...rest,
244 },
245 ])
246 );
249const kNumberTransformers = [convertToNumber];
250const kNumberValidators = [validateGreaterThan(0)];
251export function useNumberInput({ ...rest }) {
252 return useFirstOf(
253 useForm([
254 {
255 type: 'number',
256 label: 'Number',
257 autoComplete: 'off',
258 transformers: kNumberTransformers,
259 validators: kNumberValidators,
260 ...rest,
261 },
262 ])
263 );
266const kEmailValidators = [validateEmail, validateNotEmpty];
267export const buildEmailInputConfig = extra => ({
268 type: 'email',
269 autoComplete: 'off',
270 validators: kEmailValidators,
271 initialValue: '',
272 ...extra,
273});