on master help sign in
fix: clear toggled fields on hide and invalidate tx when arguments are invalid
7e6efa2Matt Condon (shrugs)
src/indigo-react/lib/useForm.js+19−7
@@ -1,4 +1,4 @@
1−import { useCallback, useMemo, useState } from 'react';
1+import { useCallback, useMemo } from 'react';
2import { isEqual, keyBy, get, every, some } from 'lodash';
2import { isEqual, keyBy, get, every, some } from 'lodash';
3
3
4import { compose } from 'lib/lib';
4import { compose } from 'lib/lib';
@@ -37,7 +37,7 @@ export default function useForm(inputConfigs = []) {
37 const byName = useMemo(() => keyBy(configs, 'name'), [configs]);
37 const byName = useMemo(() => keyBy(configs, 'name'), [configs]);
38
38
39 // track values
39 // track values
40− const [values, _setValues] = useState(() =>
40+ const [values, _setValues, clearValues] = useSetState(() =>
41 defaultsFor(configs, config =>
41 defaultsFor(configs, config =>
42 config.initialValue === undefined ? '' : config.initialValue
42 config.initialValue === undefined ? '' : config.initialValue
43 )
43 )
@@ -50,7 +50,7 @@ export default function useForm(inputConfigs = []) {
50 // until there's a traditional state update.
50 // until there's a traditional state update.
51
51
52 // track focused states
52 // track focused states
53− const [focused, setFocused] = useSetState(() =>
53+ const [focused, setFocused, clearFocused] = useSetState(() =>
54 defaultsFor(configs, config => config.autoFocus && !config.disabled)
54 defaultsFor(configs, config => config.autoFocus && !config.disabled)
55 );
55 );
56 const focuses = useMemo(
56 const focuses = useMemo(
@@ -59,10 +59,14 @@ export default function useForm(inputConfigs = []) {
59 );
59 );
60
60
61 // track whether or not input has been focused
61 // track whether or not input has been focused
62− const [hasBeenFocused, setHasBeenFocused] = useSetState({});
62+ const [hasBeenFocused, setHasBeenFocused, clearHasBeenFocused] = useSetState(
63+ {}
64+ );
63
65
64 // track whether or not the input has been touched
66 // track whether or not the input has been touched
65− const [hasBeenTouched, setHasBeenTouched] = useSetState({});
67+ const [hasBeenTouched, setHasBeenTouched, clearHasBeenTouched] = useSetState(
68+ {}
69+ );
66
70
67 // build fn that transforms a value by input name
71 // build fn that transforms a value by input name
68 const transform = useCallback(
72 const transform = useCallback(
@@ -72,11 +76,18 @@ export default function useForm(inputConfigs = []) {
72
76
73 // set a value (and transform it)
77 // set a value (and transform it)
74 const setValue = useCallback(
78 const setValue = useCallback(
75− (name, value) =>
76− _setValues(values => ({ ...values, [name]: transform(name, value) })),
79+ (name, value) => _setValues({ [name]: transform(name, value) }),
77 [_setValues, transform]
80 [_setValues, transform]
78 );
81 );
79
82
83+ // reset the form
84+ const reset = useCallback(() => {
85+ clearFocused();
86+ clearHasBeenFocused();
87+ clearHasBeenTouched();
88+ clearValues();
89+ }, [clearFocused, clearHasBeenFocused, clearHasBeenTouched, clearValues]);
90+
80 // build fn that validates a value by input name
91 // build fn that validates a value by input name
81 const validate = useCallback(
92 const validate = useCallback(
82 (name, value) =>
93 (name, value) =>
@@ -246,5 +257,6 @@ export default function useForm(inputConfigs = []) {
246 pass,
257 pass,
247 error,
258 error,
248 setValue,
259 setValue,
260+ reset,
249 };
261 };
250}
262}
src/lib/txn.js+1−1
@@ -24,7 +24,7 @@ const signTransaction = async ({
24 txn,
24 txn,
25 nonce, // number
25 nonce, // number
26 chainId, // number
26 chainId, // number
27− gasPrice, // string
27+ gasPrice, // string, in gwei
28 gasLimit, // string | number
28 gasLimit, // string | number
29}) => {
29}) => {
30 // TODO: require these in txn object
30 // TODO: require these in txn object
src/lib/useEthereumTransaction.js+5
@@ -70,6 +70,10 @@ export default function useEthereumTransaction(
70 setUnsignedTransaction,
70 setUnsignedTransaction,
71 ]);
71 ]);
72
72
73+ const unconstruct = useCallback(() => setUnsignedTransaction(undefined), [
74+ setUnsignedTransaction,
75+ ]);
76+
73 const generateAndSign = useCallback(async () => {
77 const generateAndSign = useCallback(async () => {
74 try {
78 try {
75 setError(undefined);
79 setError(undefined);
@@ -163,6 +167,7 @@ export default function useEthereumTransaction(
163 isDefaultState,
167 isDefaultState,
164 initializing,
168 initializing,
165 construct,
169 construct,
170+ unconstruct,
166 constructed,
171 constructed,
167 canSign,
172 canSign,
168 generateAndSign,
173 generateAndSign,
src/views/Admin/AdminNetworkingKeys.js+10−3
@@ -61,11 +61,13 @@ function useSetKeys() {
61 const _point = need.point(pointCursor);
61 const _point = need.point(pointCursor);
62 const _contracts = need.contracts(contracts);
62 const _contracts = need.contracts(contracts);
63
63
64+ // NOTE: nd = 'nondeterministic' (can also be a 'manual' seed)
64 const [ndNetworkSeed, setNdNetworkSeed] = useState();
65 const [ndNetworkSeed, setNdNetworkSeed] = useState();
65
66
66 const {
67 const {
67 isDefaultState,
68 isDefaultState,
68 construct: _construct,
69 construct: _construct,
70+ unconstruct,
69 broadcasting,
71 broadcasting,
70 confirmed,
72 confirmed,
71 inputsLocked,
73 inputsLocked,
@@ -131,6 +133,7 @@ function useSetKeys() {
131 return {
133 return {
132 isDefaultState,
134 isDefaultState,
133 construct,
135 construct,
136+ unconstruct,
134 broadcasting,
137 broadcasting,
135 confirmed,
138 confirmed,
136 ndNetworkSeed,
139 ndNetworkSeed,
@@ -155,6 +158,7 @@ export default function AdminNetworkingKeys() {
155 const {
158 const {
156 isDefaultState,
159 isDefaultState,
157 construct,
160 construct,
161+ unconstruct,
158 broadcasting,
162 broadcasting,
159 confirmed,
163 confirmed,
160 inputsLocked,
164 inputsLocked,
@@ -173,7 +177,7 @@ export default function AdminNetworkingKeys() {
173 const [
177 const [
174 networkSeedInput,
178 networkSeedInput,
175 { pass: validNetworkSeed, data: networkSeed },
179 { pass: validNetworkSeed, data: networkSeed },
176− { setValue: setNetworkSeed },
180+ { reset: resetNetworkSeed },
177 ] = useHexInput({
181 ] = useHexInput({
178 name: 'networkseed',
182 name: 'networkseed',
179 label: 'Network Seed (64 bytes)',
183 label: 'Network Seed (64 bytes)',
@@ -197,21 +201,24 @@ export default function AdminNetworkingKeys() {
197 validDiscontinuity
201 validDiscontinuity
198 ) {
202 ) {
199 construct(networkSeed, isDiscontinuity);
203 construct(networkSeed, isDiscontinuity);
204+ } else {
205+ unconstruct();
200 }
206 }
201 }, [
207 }, [
202 construct,
208 construct,
203 isDiscontinuity,
209 isDiscontinuity,
204 networkSeed,
210 networkSeed,
205 showNetworkSeed,
211 showNetworkSeed,
212+ unconstruct,
206 validDiscontinuity,
213 validDiscontinuity,
207 validNetworkSeed,
214 validNetworkSeed,
208 ]);
215 ]);
209
216
210 useEffect(() => {
217 useEffect(() => {
211 if (!showNetworkSeed) {
218 if (!showNetworkSeed) {
212− setNetworkSeed('');
219+ resetNetworkSeed();
213 }
220 }
214− }, [setNetworkSeed, showNetworkSeed]);
221+ }, [resetNetworkSeed, showNetworkSeed]);
215
222
216 const goRelocate = useCallback(() => push(names.RELOCATE), [push, names]);
223 const goRelocate = useCallback(() => push(names.RELOCATE), [push, names]);
217
224
src/views/Admin/AdminSetProxy.js+43−18
@@ -1,6 +1,6 @@
1−import React, { useCallback, useEffect, useState } from 'react';
1+import React, { useCallback, useEffect } from 'react';
2import cn from 'classnames';
2import cn from 'classnames';
3−import { Grid, Text, Input, Flex, LinkButton } from 'indigo-react';
3+import { Grid, Text, Input, Flex, ToggleInput } from 'indigo-react';
4import * as azimuth from 'azimuth-js';
4import * as azimuth from 'azimuth-js';
5
5
6import { useNetwork } from 'store/network';
6import { useNetwork } from 'store/network';
@@ -16,7 +16,7 @@ import * as need from 'lib/need';
16import { useLocalRouter } from 'lib/LocalRouter';
16import { useLocalRouter } from 'lib/LocalRouter';
17import { ETH_ZERO_ADDR, eqAddr } from 'lib/wallet';
17import { ETH_ZERO_ADDR, eqAddr } from 'lib/wallet';
18import capitalize from 'lib/capitalize';
18import capitalize from 'lib/capitalize';
19−import { useAddressInput } from 'lib/useInputs';
19+import { useAddressInput, useCheckboxInput } from 'lib/useInputs';
20import useEthereumTransaction from 'lib/useEthereumTransaction';
20import useEthereumTransaction from 'lib/useEthereumTransaction';
21
21
22import ViewHeader from 'components/ViewHeader';
22import ViewHeader from 'components/ViewHeader';
@@ -51,8 +51,6 @@ function useSetProxy(proxyType) {
51 const _contracts = need.contracts(contracts);
51 const _contracts = need.contracts(contracts);
52 const _point = need.point(pointCursor);
52 const _point = need.point(pointCursor);
53
53
54− const [isUnsetting, setUnsetting] = useState(false);
55−
56 const buildUnsignedTx = useCallback(
54 const buildUnsignedTx = useCallback(
57 address => {
55 address => {
58 const txArgs = [_contracts, _point, address];
56 const txArgs = [_contracts, _point, address];
@@ -75,6 +73,7 @@ function useSetProxy(proxyType) {
75
73
76 const {
74 const {
77 construct: _construct,
75 construct: _construct,
76+ unconstruct,
78 confirmed,
77 confirmed,
79 inputsLocked,
78 inputsLocked,
80 bind,
79 bind,
@@ -95,16 +94,15 @@ function useSetProxy(proxyType) {
95
94
96 // force-unset
95 // force-unset
97 const unset = useCallback(() => {
96 const unset = useCallback(() => {
98− setUnsetting(true);
99 _construct(buildUnsignedTx(ETH_ZERO_ADDR));
97 _construct(buildUnsignedTx(ETH_ZERO_ADDR));
100 }, [_construct, buildUnsignedTx]);
98 }, [_construct, buildUnsignedTx]);
101
99
102 return {
100 return {
103 construct,
101 construct,
102+ unconstruct,
104 confirmed,
103 confirmed,
105 unset,
104 unset,
106− isUnsetting,
107− inputsLocked: inputsLocked || isUnsetting,
105+ inputsLocked: inputsLocked,
108 bind,
106 bind,
109 };
107 };
110}
108}
@@ -121,20 +119,49 @@ export default function AdminSetProxy() {
121
119
122 const properProxyType = capitalize(proxyTypeToHuman(data.proxyType));
120 const properProxyType = capitalize(proxyTypeToHuman(data.proxyType));
123
121
124− const { construct, unset, inputsLocked, confirmed, bind } = useSetProxy(
122+ const {
123+ construct,
124+ unconstruct,
125+ unset,
126+ inputsLocked,
127+ confirmed,
128+ bind,
125− data.proxyType
129+ } = useSetProxy(data.proxyType);
130+
131+ const [unsetInput, { data: isUnsetting }] = useCheckboxInput({
132+ name: 'unset',
133+ label: 'Unset',
134+ inverseLabel: 'Specify',
135+ initialValue: false,
126− );
136+ });
137+ const [
138+ addressInput,
139+ { pass: validAddress, data: address },
140+ { reset: resetAddress },
127− const [addressInput, { pass, data: address }] = useAddressInput({
141+ ] = useAddressInput({
128 name: 'address',
142 name: 'address',
129 label: `New ${properProxyType} Address`,
143 label: `New ${properProxyType} Address`,
130− disabled: inputsLocked,
144+ disabled: inputsLocked || isUnsetting,
131 });
145 });
132
146
133 useEffect(() => {
147 useEffect(() => {
134− if (pass) {
148+ if (isUnsetting) {
149+ unset();
150+ resetAddress();
151+ } else if (validAddress) {
135 construct(address);
152 construct(address);
153+ } else {
154+ unconstruct();
136 }
155 }
156+ }, [
157+ validAddress,
158+ address,
137− }, [pass, address, construct]);
159+ construct,
160+ isUnsetting,
161+ unset,
162+ unconstruct,
163+ resetAddress,
164+ ]);
138
165
139 const proxyAddress = proxyFromDetails(_details, _contracts, data.proxyType);
166 const proxyAddress = proxyFromDetails(_details, _contracts, data.proxyType);
140 const isProxySet = !eqAddr(ETH_ZERO_ADDR, proxyAddress);
167 const isProxySet = !eqAddr(ETH_ZERO_ADDR, proxyAddress);
@@ -176,9 +203,7 @@ export default function AdminSetProxy() {
176 {isProxySet ? proxyAddress : 'Unset'}
203 {isProxySet ? proxyAddress : 'Unset'}
177 </Flex.Item>
204 </Flex.Item>
178 {!confirmed && isProxySet && (
205 {!confirmed && isProxySet && (
179− <Flex.Item as={LinkButton} onClick={unset} disabled={inputsLocked}>
206+ <Flex.Item as={ToggleInput} {...unsetInput} />
180− Unset
181− </Flex.Item>
182 )}
207 )}
183 </Grid.Item>
208 </Grid.Item>
184
209
src/views/Admin/AdminTransfer.js+13−2
@@ -28,6 +28,7 @@ function useTransfer() {
28
28
29 const {
29 const {
30 construct: _construct,
30 construct: _construct,
31+ unconstruct,
31 confirmed,
32 confirmed,
32 inputsLocked,
33 inputsLocked,
33 bind,
34 bind,
@@ -50,6 +51,7 @@ function useTransfer() {
50
51
51 return {
52 return {
52 construct,
53 construct,
54+ unconstruct,
53 confirmed,
55 confirmed,
54 inputsLocked,
56 inputsLocked,
55 bind,
57 bind,
@@ -60,7 +62,14 @@ export default function AdminTransfer() {
60 const { pop } = useLocalRouter();
62 const { pop } = useLocalRouter();
61 const name = useCurrentPointName();
63 const name = useCurrentPointName();
62
64
63− const { construct, confirmed, inputsLocked, bind } = useTransfer();
65+ const {
66+ construct,
67+ unconstruct,
68+ confirmed,
69+ inputsLocked,
70+ bind,
71+ } = useTransfer();
72+
64 const [addressInput, { pass, data: address }] = useAddressInput({
73 const [addressInput, { pass, data: address }] = useAddressInput({
65 name: 'address',
74 name: 'address',
66 label: `Ethereum Address`,
75 label: `Ethereum Address`,
@@ -70,8 +79,10 @@ export default function AdminTransfer() {
70 useEffect(() => {
79 useEffect(() => {
71 if (pass) {
80 if (pass) {
72 construct(address);
81 construct(address);
82+ } else {
83+ unconstruct();
73 }
84 }
74− }, [pass, address, construct]);
85+ }, [pass, address, construct, unconstruct]);
75
86
76 return (
87 return (
77 <Grid>
88 <Grid>
src/views/Party/PartySetPoolSize.js+12−2
@@ -31,6 +31,7 @@ function useSetPoolSize() {
31
31
32 const {
32 const {
33 construct: _construct,
33 construct: _construct,
34+ unconstruct,
34 confirmed,
35 confirmed,
35 inputsLocked,
36 inputsLocked,
36 bind,
37 bind,
@@ -59,6 +60,7 @@ function useSetPoolSize() {
59
60
60 return {
61 return {
61 construct,
62 construct,
63+ unconstruct,
62 confirmed,
64 confirmed,
63 inputsLocked,
65 inputsLocked,
64 bind,
66 bind,
@@ -73,7 +75,13 @@ export default function PartySetPoolSize() {
73
75
74 const { spawnIsDelegatedSending } = useCurrentPermissions();
76 const { spawnIsDelegatedSending } = useCurrentPermissions();
75
77
78+ const {
79+ construct,
80+ unconstruct,
81+ confirmed,
82+ inputsLocked,
83+ bind,
76− const { construct, confirmed, inputsLocked, bind } = useSetPoolSize();
84+ } = useSetPoolSize();
77
85
78 const [poolOwnerInput, { pass: validPoint, data: poolOwner }] = usePointInput(
86 const [poolOwnerInput, { pass: validPoint, data: poolOwner }] = usePointInput(
79 {
87 {
@@ -96,8 +104,10 @@ export default function PartySetPoolSize() {
96 useEffect(() => {
104 useEffect(() => {
97 if (validPoint && validPoolSize) {
105 if (validPoint && validPoolSize) {
98 construct(patp2dec(poolOwner), poolSize);
106 construct(patp2dec(poolOwner), poolSize);
107+ } else {
108+ unconstruct();
99 }
109 }
100− }, [poolOwner, construct, validPoint, validPoolSize, poolSize]);
110+ }, [poolOwner, construct, validPoint, validPoolSize, poolSize, unconstruct]);
101
111
102 return (
112 return (
103 <View inset>
113 <View inset>