on dffcebf help sign in
bridge/src/lib/useInputs.js
260 lines 6.0 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 kPointValidators = [
149 validatePoint,
150 validateMaximumPatpByteLength(4),
151 validateNotEmpty,
152];
153const kPointTransformers = [prependSig];
154export function usePointInput(rest) {
155 return useFirstOf(
156 useForm([
157 {
158 type: 'text',
159 label: 'Point',
160 placeholder: 'e.g. ~zod',
161 validators: kPointValidators,
162 transformers: kPointTransformers,
163 mono: true,
164 ...rest,
165 },
166 ]),
167 ({ error, pass, focused, data }) => ({
168 accessory: data ? (
169 <InputSigil
170 patp={data}
171 size={44}
172 margin={8}
173 pass={pass}
174 focused={focused}
175 error={error}
176 />
177 ) : null,
178 })
179 );
182export function useCheckboxInput({ initialValue, ...rest }) {
183 return useFirstOf(
184 useForm([
185 {
186 type: 'checkbox',
187 ...rest,
188 },
189 ])
190 );
193export function useSelectInput({ initialValue, options, ...rest }) {
194 return useFirstOf(
195 useForm([
196 {
197 type: 'select',
198 validators: useMemo(
199 () => [
200 validateOneOf(
201 options.map(option => option.value),
202 validateNotEmpty
203 ),
204 ],
205 [options]
206 ),
207 options,
208 initialValue: initialValue || options[0].value,
209 ...rest,
210 },
211 ])
212 );
215const kAddressValidators = [
216 validateEthereumAddress,
217 validateNotNullAddress,
218 validateNotEmpty,
219];
220export function useAddressInput({ ...rest }) {
221 return useFirstOf(
222 useForm([
223 {
224 type: 'string',
225 label: 'Ethereum Address',
226 placeholder: 'e.g. 0x6DEfFb0caFDB11D175F123F6891AA64F01c24F7d',
227 autoComplete: 'off',
228 validators: kAddressValidators,
229 mono: true,
230 ...rest,
231 },
232 ])
233 );
236const kNumberTransformers = [convertToNumber];
237const kNumberValidators = [validateGreaterThan(0)];
238export function useNumberInput({ ...rest }) {
239 return useFirstOf(
240 useForm([
241 {
242 type: 'number',
243 label: 'Number',
244 autoComplete: 'off',
245 transformers: kNumberTransformers,
246 validators: kNumberValidators,
247 ...rest,
248 },
249 ])
250 );
253const kEmailValidators = [validateEmail, validateNotEmpty];
254export const buildEmailInputConfig = extra => ({
255 type: 'email',
256 autoComplete: 'off',
257 validators: kEmailValidators,
258 initialValue: '',
259 ...extra,
260});