on master help sign in
chore: form-ify inlinethtransction and netowrking keys
commit329f1a925ec78a0cc09a550c34886f0a086c2aa0 authorMatt Condon (shrugs) <mattgcondon@gmail.com> dateMon 5 Aug 2019, 12:24 (7 years ago) parent26f6d3e
5 files changed+216−219
src/components/InlineEthereumTransaction.js+127−106 src/form/Inputs.js+2−2 src/lib/useInputs.js−34 src/views/Admin/AdminNetworkingKeys.js+85−71 src/views/Login/PrivateKey.js+2−6
src/components/InlineEthereumTransaction.js+127−106
@@ -1,4 +1,4 @@
1−import React, { useEffect } from 'react';
1+import React, { useMemo, useCallback } from 'react';
22import cn from 'classnames';
33import {
44 Grid,
@@ -12,12 +12,14 @@ import {
1212} from 'indigo-react';
1313import { fromWei } from 'web3-utils';
1414
15−import { useCheckboxInput } from 'lib/useInputs';
1615import { useExploreTxUrl } from 'lib/explorer';
1716import { hexify } from 'lib/txn';
1817
1918import { GenerateButton, ForwardButton, RestartButton } from './Buttons';
2019import WarningBox from './WarningBox';
20+import { composeValidator, buildCheckboxValidator } from 'form/Inputs';
21+import BridgeForm from 'form/BridgeForm';
22+import Condition from 'form/Condition';
2123
2224export default function InlineEthereumTransaction({
2325 // from useEthereumTransaction.bind
@@ -57,29 +59,24 @@ export default function InlineEthereumTransaction({
5759 const showSignedTx = signed;
5860
5961 const exploreTxUrl = useExploreTxUrl(txHash);
60− const [advancedInput, { data: advancedOpen }] = useCheckboxInput({
61− name: 'advanced',
62− label: 'Advanced Configuration',
63− inverseLabel: 'Cancel Advanced Configuration',
64− disabled: !showConfigureInput,
65− });
6662
67− const [
68− viewSignedTransaction,
69− { data: signedTransactionOpen },
70− ] = useCheckboxInput({
71− name: 'viewsigned',
72− label: 'View Signed Transaction',
73− inverseLabel: 'Hide Signed Transaction',
74− disabled: !showSignedTx,
75− });
63+ const validate = useMemo(
64+ () =>
65+ composeValidator({
66+ useAdvanced: buildCheckboxValidator(),
67+ viewSigned: buildCheckboxValidator(),
68+ }),
69+ []
70+ );
7671
77− // reset gas price when closing advanced configuration
78− useEffect(() => {
79− if (!advancedOpen) {
80− resetGasPrice();
81− }
82− }, [advancedOpen, resetGasPrice]);
72+ const onValues = useCallback(
73+ ({ valid, values, form }) => {
74+ if (!values.useAdvanced) {
75+ resetGasPrice();
76+ }
77+ },
78+ [resetGasPrice]
79+ );
8380
8481 const renderPrimaryButton = () => {
8582 if (error) {
@@ -126,94 +123,118 @@ export default function InlineEthereumTransaction({
126123
127124 return (
128125 <Grid className={cn(className, 'mt1')}>
129− {renderPrimaryButton()}
126+ <BridgeForm validate={validate} onValues={onValues} onSubmit={() => {}}>
127+ {({ handleSubmit }) => (
128+ <>
129+ {renderPrimaryButton()}
130130
131− {error && (
132− <Grid.Item full as={ErrorText} className="mt1">
133− {error.message}
134− </Grid.Item>
135− )}
136−
137− {needFunds && (
138− <Grid.Item full as={WarningBox} className="mt3">
139− The address {needFunds.address} needs at least{' '}
140− {fromWei(needFunds.minBalance)} ETH and currently has{' '}
141− {fromWei(needFunds.balance)} ETH. Waiting until the account has enough
142− funds.
143− </Grid.Item>
144− )}
145−
146− {showConfigureInput && (
147− <>
148− <Grid.Item full as={ToggleInput} {...advancedInput} />
149− {advancedOpen && (
150− <>
151− <Grid.Item full as={Flex} row justify="between" className="mt2">
152− <Flex.Item as={H5}>Gas Price</Flex.Item>
153− <Flex.Item as={H5}>{gasPrice} Gwei</Flex.Item>
154− </Grid.Item>
155− {/* TODO(shrugs): move to indigo/RangeInput */}
156− <Grid.Item
157− full
158− as="input"
159− type="range"
160− min="1"
161− max="100"
162− value={gasPrice}
163− onChange={e => setGasPrice(parseInt(e.target.value, 10))}
164− />
165− <Grid.Item
166− full
167− as={Flex}
168− row
169− justify="between"
170− className="f6 mt1">
171− <Flex.Item as={Text}>Cheap</Flex.Item>
172− <Flex.Item as={Text}>Fast</Flex.Item>
131+ {error && (
132+ <Grid.Item full as={ErrorText} className="mt1">
133+ {error.message}
173134 </Grid.Item>
174− <Grid.Item full as={HelpText} className="mt3">
175− Nonce: {nonce}
176− </Grid.Item>
177− <Grid.Item full as={HelpText} className="mt1">
178− Chain ID: {chainId}
135+ )}
136+
137+ {needFunds && (
138+ <Grid.Item full as={WarningBox} className="mt3">
139+ The address {needFunds.address} needs at least{' '}
140+ {fromWei(needFunds.minBalance)} ETH and currently has{' '}
141+ {fromWei(needFunds.balance)} ETH. Waiting until the account has
142+ enough funds.
179143 </Grid.Item>
180− <Grid.Divider className="mt3" />
181− </>
182− )}
183− </>
184− )}
144+ )}
185145
186− {showSignedTx && (
187− <>
188− <Grid.Item full as={ToggleInput} {...viewSignedTransaction} />
189− {signedTransactionOpen && (
190− <Grid.Item full as="code" className="f6 mono gray4 wrap">
191− {hexify(signedTransaction.serialize())}
192− </Grid.Item>
193− )}
194− </>
195− )}
146+ {showConfigureInput && (
147+ <>
148+ <Grid.Item
149+ full
150+ as={ToggleInput}
151+ name="useAdvanced"
152+ label="Advanced Configuration"
153+ inverseLabel="Cancel Advanced Configuration"
154+ disabled={!showConfigureInput}
155+ />
196156
197− {showReceipt && (
198− <>
199− <Grid.Divider />
200− <Grid.Item full as={Flex} col className="pv4">
201− <Flex.Item as={Flex} row justify="between">
202− <Flex.Item as={H5}>Transaction Hash</Flex.Item>
203− <Flex.Item as={LinkButton} href={exploreTxUrl}>
204− Etherscan↗
205− </Flex.Item>
206− </Flex.Item>
207− <Flex.Item as={Flex}>
208− <Flex.Item flex as="code" className="f6 mono gray4 wrap">
209− {txHash}
210− </Flex.Item>
211− <Flex.Item flex />
212− </Flex.Item>
213− </Grid.Item>
214− <Grid.Divider />
215− </>
216− )}
157+ <Condition when="useAdvanced" is={true}>
158+ <Grid.Item
159+ full
160+ as={Flex}
161+ row
162+ justify="between"
163+ className="mt2">
164+ <Flex.Item as={H5}>Gas Price</Flex.Item>
165+ <Flex.Item as={H5}>{gasPrice} Gwei</Flex.Item>
166+ </Grid.Item>
167+ {/* TODO(shrugs): move to indigo/RangeInput */}
168+ <Grid.Item
169+ full
170+ as="input"
171+ type="range"
172+ min="1"
173+ max="100"
174+ value={gasPrice}
175+ onChange={e => setGasPrice(parseInt(e.target.value, 10))}
176+ />
177+ <Grid.Item
178+ full
179+ as={Flex}
180+ row
181+ justify="between"
182+ className="f6 mt1">
183+ <Flex.Item as={Text}>Cheap</Flex.Item>
184+ <Flex.Item as={Text}>Fast</Flex.Item>
185+ </Grid.Item>
186+ <Grid.Item full as={HelpText} className="mt3">
187+ Nonce: {nonce}
188+ </Grid.Item>
189+ <Grid.Item full as={HelpText} className="mt1">
190+ Chain ID: {chainId}
191+ </Grid.Item>
192+ <Grid.Divider className="mt3" />
193+ </Condition>
194+ </>
195+ )}
196+
197+ {showSignedTx && (
198+ <>
199+ <Grid.Item
200+ full
201+ as={ToggleInput}
202+ name="viewSigned"
203+ label="View Signed Transaction"
204+ inverseLabel="Hide Signed Transaction"
205+ disabled={!showSignedTx}
206+ />
207+ <Condition when="viewSigned" is={true}>
208+ <Grid.Item full as="code" className="f6 mono gray4 wrap">
209+ {hexify(signedTransaction.serialize())}
210+ </Grid.Item>
211+ </Condition>
212+ </>
213+ )}
214+
215+ {showReceipt && (
216+ <>
217+ <Grid.Divider />
218+ <Grid.Item full as={Flex} col className="pv4">
219+ <Flex.Item as={Flex} row justify="between">
220+ <Flex.Item as={H5}>Transaction Hash</Flex.Item>
221+ <Flex.Item as={LinkButton} href={exploreTxUrl}>
222+ Etherscan↗
223+ </Flex.Item>
224+ </Flex.Item>
225+ <Flex.Item as={Flex}>
226+ <Flex.Item flex as="code" className="f6 mono gray4 wrap">
227+ {txHash}
228+ </Flex.Item>
229+ <Flex.Item flex />
230+ </Flex.Item>
231+ </Grid.Item>
232+ <Grid.Divider />
233+ </>
234+ )}
235+ </>
236+ )}
237+ </BridgeForm>
217238 </Grid>
218239 );
219240}
src/form/Inputs.js+2−2
@@ -94,7 +94,6 @@ export const composeValidator = (
9494 };
9595
9696 return async values => {
97− console.log('validating...');
9897 const errors = await fieldLevelValidator(values);
9998 return await formValidator(values, errors);
10099 };
@@ -195,12 +194,13 @@ export function PointInput({ name, size = 4, ...rest }) {
195194 );
196195}
197196
198−export function PrivateKeyInput({ ...rest }) {
197+export function HexInput({ ...rest }) {
199198 return (
200199 <Input
201200 type="text"
202201 placeholder={PLACEHOLDER_PRIVATE_KEY}
203202 autoComplete="off"
203+ mono
204204 {...rest}
205205 />
206206 );
src/lib/useInputs.js−34
@@ -57,40 +57,6 @@ function useFirstOf({ inputs, setValue, ...rest }, mapper = identity) {
5757 ];
5858}
5959
60−export function usePassphraseInput(props) {
61− return useFirstOf(
62− useForm([
63− {
64− type: 'password',
65− autoComplete: 'off',
66− placeholder: 'Passphrase',
67− ...props,
68− },
69− ])
70− );
71−}
72−
73−export function useHexInput({ length, ...rest }) {
74− return useFirstOf(
75− useForm([
76− {
77− type: 'text', // or password
78− autoComplete: 'off',
79− placeholder: PLACEHOLDER_PRIVATE_KEY,
80− validators: useMemo(
81− () => [
82− validateHexString,
83− validateLength(length + 2),
84− validateNotEmpty,
85− ],
86− [length]
87− ),
88− ...rest,
89− },
90− ])
91− );
92−}
93−
9460const kMnemonicValidators = [validateMnemonic, validateNotEmpty];
9561export function useMnemonicInput(props) {
9662 return useFirstOf(
src/views/Admin/AdminNetworkingKeys.js+85−71
@@ -1,14 +1,6 @@
1−import React, { useCallback, useEffect, useRef, useState } from 'react';
1+import React, { useCallback, useMemo, useRef, useState } from 'react';
22import { Just, Nothing } from 'folktale/maybe';
3−import {
4− Grid,
5− Text,
6− H5,
7− Flex,
8− ToggleInput,
9− Input,
10− CheckboxInput,
11−} from 'indigo-react';
3+import { Grid, Text, H5, Flex, ToggleInput, CheckboxInput } from 'indigo-react';
124import * as azimuth from 'azimuth-js';
135import { randomHex } from 'web3-utils';
146
@@ -30,7 +22,6 @@ import { formatDotsWithTime } from 'lib/dateFormat';
3022import useEthereumTransaction from 'lib/useEthereumTransaction';
3123import { GAS_LIMITS } from 'lib/constants';
3224import { addHexPrefix } from 'lib/wallet';
33−import { useHexInput, useCheckboxInput } from 'lib/useInputs';
3425import useKeyfileGenerator from 'lib/useKeyfileGenerator';
3526
3627import ViewHeader from 'components/ViewHeader';
@@ -40,6 +31,15 @@ import DownloadKeyfileButton from 'components/DownloadKeyfileButton';
4031import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
4132import NoticeBox from 'components/NoticeBox';
4233
34+import {
35+ composeValidator,
36+ buildCheckboxValidator,
37+ buildHexValidator,
38+ HexInput,
39+} from 'form/Inputs';
40+import BridgeForm from 'form/BridgeForm';
41+import Condition from 'form/Condition';
42+
4343const chainKeyProp = name => d =>
4444 d[name] === CURVE_ZERO_ADDR ? Nothing() : Just(d[name]);
4545
@@ -163,58 +163,44 @@ export default function AdminNetworkingKeys() {
163163 keyfileBind,
164164 } = useSetKeys();
165165
166− const [showNetworkSeedInput, { data: showNetworkSeed }] = useCheckboxInput({
167− name: 'shownetworkseed',
168− label: 'Use Custom Network Seed',
169− inverseLabel: 'Back to Derived Network Seed',
170− initialValue: false,
171− disabled: inputsLocked,
172− });
166+ const validateForm = useCallback((values, errors) => {
167+ if (values.useNetworkSeed && errors.networkSeed) {
168+ return errors;
169+ }
173170
174− const [
175− networkSeedInput,
176− { pass: validNetworkSeed, data: networkSeed },
177− { reset: resetNetworkSeed },
178− ] = useHexInput({
179− name: 'networkseed',
180− label: 'Network Seed (64 bytes)',
181− length: 32, // 64 bytes
182− disabled: inputsLocked,
183− });
171+ return {};
172+ }, []);
173+
174+ const validate = useMemo(
175+ () =>
176+ composeValidator(
177+ {
178+ useNetworkSeed: buildCheckboxValidator(),
179+ networkSeed: buildHexValidator(32),
180+ useDiscontinuity: buildCheckboxValidator(),
181+ },
182+ validateForm
183+ ),
184+ [validateForm]
185+ );
184186
185− const [
186− discontinuityInput,
187− { pass: validDiscontinuity, data: isDiscontinuity },
188− ] = useCheckboxInput({
189− name: 'discontinuity',
190− label: 'Trigger New Continuity Era',
191− initialValue: false,
192− disabled: inputsLocked,
193− });
187+ const onValues = useCallback(
188+ ({ valid, values, form }) => {
189+ if (valid) {
190+ construct(
191+ values.useNetworkSeed ? values.networkSeed : undefined,
192+ values.useDiscontinuity
193+ );
194+ } else {
195+ unconstruct();
196+ }
194197
195− useEffect(() => {
196− const nothingOrValidSeed =
197− !showNetworkSeed || (showNetworkSeed && validNetworkSeed);
198− if (nothingOrValidSeed && validDiscontinuity) {
199− construct(networkSeed, isDiscontinuity);
200− } else {
201− unconstruct();
202− }
203− }, [
204− construct,
205− unconstruct,
206− isDiscontinuity,
207− networkSeed,
208− showNetworkSeed,
209− validDiscontinuity,
210− validNetworkSeed,
211− ]);
212−
213− useEffect(() => {
214− if (!showNetworkSeed) {
215− resetNetworkSeed();
216− }
217− }, [resetNetworkSeed, showNetworkSeed]);
198+ if (!values.useNetworkSeed && values.networkSeed) {
199+ form.change('networkSeed', '');
200+ }
201+ },
202+ [construct, unconstruct]
203+ );
218204
219205 const goRelocate = useCallback(() => push(names.RELOCATE), [push, names]);
220206
@@ -335,20 +321,48 @@ export default function AdminNetworkingKeys() {
335321 )}
336322
337323 {!completed && (
338− <>
339− <Grid.Item full as={ToggleInput} {...showNetworkSeedInput} />
340− {showNetworkSeed && (
324+ <BridgeForm
325+ validate={validate}
326+ onValues={onValues}
327+ onSubmit={() => {}}
328+ initialValues={{
329+ useNetworkSeed: false,
330+ useDiscontinuity: false,
331+ }}>
332+ {({ handleSubmit }) => (
341333 <>
342− <Grid.Item full as={NoticeBox} className="mb2">
343− When using a custom network seed, you'll need to download your
344− Arvo keyfile immediately after this transaction is
345− completed—Multipass does not store your seed.
346− </Grid.Item>
347− <Grid.Item full as={Input} {...networkSeedInput} />
334+ <Grid.Item
335+ full
336+ as={ToggleInput}
337+ name="useNetworkSeed"
338+ label="Use Custom Network Seed"
339+ inverseLabel="Back to Derived Network Seed"
340+ disabled={inputsLocked}
341+ />
342+ <Condition when="useNetworkSeed" is={true}>
343+ <Grid.Item full as={NoticeBox} className="mb2">
344+ When using a custom network seed, you'll need to download
345+ your Arvo keyfile immediately after this transaction is
346+ completed—Multipass does not store your seed.
347+ </Grid.Item>
348+ <Grid.Item
349+ full
350+ as={HexInput}
351+ name="networkSeed"
352+ label="Network Seed (64 bytes)"
353+ disabled={inputsLocked}
354+ />
355+ </Condition>
356+ <Grid.Item
357+ full
358+ as={CheckboxInput}
359+ name="useDiscontinuity"
360+ label="Trigger New Continuity Era"
361+ disabled={inputsLocked}
362+ />
348363 </>
349364 )}
350− <Grid.Item full as={CheckboxInput} {...discontinuityInput} />
351− </>
365+ </BridgeForm>
352366 )}
353367
354368 <Grid.Item
src/views/Login/PrivateKey.js+2−6
@@ -9,11 +9,7 @@ import useLoginView from 'lib/useLoginView';
99import FormError from 'form/FormError';
1010import ContinueButton from './ContinueButton';
1111import BridgeForm from 'form/BridgeForm';
12−import {
13− buildHexValidator,
14− composeValidator,
15− PrivateKeyInput,
16−} from 'form/Inputs';
12+import { buildHexValidator, composeValidator, HexInput } from 'form/Inputs';
1713
1814export default function PrivateKey({ className, goHome }) {
1915 useLoginView(WALLET_TYPES.PRIVATE_KEY);
@@ -48,7 +44,7 @@ export default function PrivateKey({ className, goHome }) {
4844 <>
4945 <Grid.Item
5046 full
51− as={PrivateKeyInput}
47+ as={HexInput}
5248 name="privatekey"
5349 label="Private key"
5450 />