diff --git a/package-lock.json b/package-lock.json index 2eaf67948..d5856a8a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2143,6 +2143,14 @@ "@xtuc/long": "4.2.2" } }, + "@welldone-software/why-did-you-render": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@welldone-software/why-did-you-render/-/why-did-you-render-3.2.3.tgz", + "integrity": "sha512-dUMkjsVsCfIo+IEmiMyb/FGKVbXrzbGqLvDTCn0ad5drNmD+BGoU/7Z+Nc7ckkV0F1G4vzs1XQrAcIxyPa8Ssw==", + "requires": { + "lodash": "^4" + } + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -2160,7 +2168,7 @@ "dev": true }, "PaperCollateralRenderer": { - "version": "github:urbit/PaperCollateralRenderer#27bd1381529618fb067a35575f8191d04ecfe10b", + "version": "github:urbit/PaperCollateralRenderer#8478aaf397e12769171b7b14332c47e4457e1d46", "from": "github:urbit/PaperCollateralRenderer#rc-immediate", "requires": { "babel-polyfill": "^6.26.0", @@ -6918,6 +6926,24 @@ } } }, + "final-form": { + "version": "4.18.4", + "resolved": "https://registry.npmjs.org/final-form/-/final-form-4.18.4.tgz", + "integrity": "sha512-UUymL6UykjwO2yUN3EhBdw8ajaa448/CczgXvLcyXwbHRjWbA3Yjdxm6WSHQBx4pLv4iEqkvmPRnQ+xmS9GUcA==", + "requires": { + "@babel/runtime": "^7.3.1" + } + }, + "final-form-arrays": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/final-form-arrays/-/final-form-arrays-3.0.1.tgz", + "integrity": "sha512-GKXecufCNCjDcz1+3peL21LuuTlApoxCcnpOnmfeJfC3xAlFKGdytYMfifP7W1IEWTGC8twTv3zItESkej8qpg==" + }, + "final-form-set-field-data": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/final-form-set-field-data/-/final-form-set-field-data-1.0.2.tgz", + "integrity": "sha512-gAnENimyQ5GW3OEGca5pbwm4lYshW2orzfBlPUYqzcm7ZxkQrVO8FqCAgEcCM+Rq9U1OU0q+D+UkqETvvDY6jw==" + }, "finalhandler": { "version": "1.1.1", "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", @@ -14511,6 +14537,43 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.6.tgz", "integrity": "sha512-X1Y+0jR47ImDVr54Ab6V9eGk0Hnu7fVWGeHQSOXHf/C2pF9c6uy3gef8QUeuUiWlNb0i08InPSE5a/KJzNzw1Q==" }, + "react-final-form": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/react-final-form/-/react-final-form-6.3.0.tgz", + "integrity": "sha512-jijhXR1fFGUBQwNOSqF4MK8XJO7Ynl1p8vcFsnQS0INSkGI52+4IagjUgtHj3w8EviIHPFK/Eflji6FELUl07w==", + "requires": { + "@babel/runtime": "^7.4.5", + "ts-essentials": "^2.0.8" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz", + "integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + } + } + }, + "react-final-form-arrays": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-final-form-arrays/-/react-final-form-arrays-3.1.0.tgz", + "integrity": "sha512-eJdAlhTKzlDD/d1wedD592a99eJNGO0e9GzY++RLN99P23cMGKSzCmsiGWLPwpY0H/C/LmNSL4XzWyH/aZembA==", + "requires": { + "@babel/runtime": "^7.4.5" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz", + "integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + } + } + }, "react-hot-loader": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.12.0.tgz", @@ -14721,6 +14784,11 @@ "regenerate": "^1.4.0" } }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" + }, "regenerator-transform": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.0.tgz", @@ -17199,6 +17267,11 @@ "solc": "0.4.24" } }, + "ts-essentials": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-2.0.12.tgz", + "integrity": "sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==" + }, "ts-pnp": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.2.tgz", diff --git a/package.json b/package.json index 198030d7f..03854e692 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@hot-loader/react-dom": "^16.8.6", "@ledgerhq/hw-app-eth": "^4.61.0", "@ledgerhq/hw-transport-u2f": "^4.61.0", + "@welldone-software/why-did-you-render": "^3.2.3", "PaperCollateralRenderer": "github:urbit/PaperCollateralRenderer#rc-immediate", "async-retry": "^1.2.3", "azimuth-js": "^0.15.1", @@ -28,6 +29,9 @@ "ethereum-blockies-base64": "^1.0.2", "ethereumjs-tx": "^1.3.7", "file-saver": "^2.0.0", + "final-form": "^4.18.4", + "final-form-arrays": "^3.0.1", + "final-form-set-field-data": "^1.0.2", "folktale": "^2.3.1", "jszip": "^3.1.5", "keccak": "^1.4.0", @@ -38,6 +42,8 @@ "react": "^16.8.6", "react-app-rewire-hot-loader": "^2.0.1", "react-dom": "^16.8.6", + "react-final-form": "^6.3.0", + "react-final-form-arrays": "^3.1.0", "react-hot-loader": "^4.12.0", "react-scripts": "3.0.1", "react-teleporter": "^1.1.0", diff --git a/src/components/Accordion.js b/src/components/Accordion.js index b16741dd1..da165e06f 100644 --- a/src/components/Accordion.js +++ b/src/components/Accordion.js @@ -2,11 +2,13 @@ import React from 'react'; import { Grid, AccessoryIcon } from 'indigo-react'; export default function Accordion({ + className, views, options, currentTab, onTabChange, - className, + + // Tab props ...rest }) { const Tab = views[currentTab]; @@ -37,7 +39,7 @@ export default function Accordion({ - {isActive && } + {isActive && } ); diff --git a/src/components/Highlighted.js b/src/components/Highlighted.js index e31679624..8db9dd0a3 100644 --- a/src/components/Highlighted.js +++ b/src/components/Highlighted.js @@ -1,13 +1,21 @@ import React from 'react'; import cn from 'classnames'; -export default function Highlighted({ warning = false, ...rest }) { +export default function Highlighted({ + as: As = 'span', + className, + warning = false, + ...rest +}) { return ( - ); diff --git a/src/components/InlineEthereumTransaction.js b/src/components/InlineEthereumTransaction.js index 2021908cf..8c7460e1c 100644 --- a/src/components/InlineEthereumTransaction.js +++ b/src/components/InlineEthereumTransaction.js @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useMemo, useCallback } from 'react'; import cn from 'classnames'; import { Grid, @@ -12,12 +12,14 @@ import { } from 'indigo-react'; import { fromWei } from 'web3-utils'; -import { useCheckboxInput } from 'lib/useInputs'; import { useExploreTxUrl } from 'lib/explorer'; import { hexify } from 'lib/txn'; import { GenerateButton, ForwardButton, RestartButton } from './Buttons'; import WarningBox from './WarningBox'; +import { composeValidator, buildCheckboxValidator } from 'form/validators'; +import BridgeForm from 'form/BridgeForm'; +import Condition from 'form/Condition'; export default function InlineEthereumTransaction({ // from useEthereumTransaction.bind @@ -57,29 +59,24 @@ export default function InlineEthereumTransaction({ const showSignedTx = signed; const exploreTxUrl = useExploreTxUrl(txHash); - const [advancedInput, { data: advancedOpen }] = useCheckboxInput({ - name: 'advanced', - label: 'Advanced Configuration', - inverseLabel: 'Cancel Advanced Configuration', - disabled: !showConfigureInput, - }); - const [ - viewSignedTransaction, - { data: signedTransactionOpen }, - ] = useCheckboxInput({ - name: 'viewsigned', - label: 'View Signed Transaction', - inverseLabel: 'Hide Signed Transaction', - disabled: !showSignedTx, - }); + const validate = useMemo( + () => + composeValidator({ + useAdvanced: buildCheckboxValidator(), + viewSigned: buildCheckboxValidator(), + }), + [] + ); - // reset gas price when closing advanced configuration - useEffect(() => { - if (!advancedOpen) { - resetGasPrice(); - } - }, [advancedOpen, resetGasPrice]); + const onValues = useCallback( + ({ valid, values, form }) => { + if (!values.useAdvanced) { + resetGasPrice(); + } + }, + [resetGasPrice] + ); const renderPrimaryButton = () => { if (error) { @@ -126,94 +123,118 @@ export default function InlineEthereumTransaction({ return ( - {renderPrimaryButton()} + + {({ handleSubmit }) => ( + <> + {renderPrimaryButton()} - {error && ( - - {error.message} - - )} - - {needFunds && ( - - The address {needFunds.address} needs at least{' '} - {fromWei(needFunds.minBalance)} ETH and currently has{' '} - {fromWei(needFunds.balance)} ETH. Waiting until the account has enough - funds. - - )} - - {showConfigureInput && ( - <> - - {advancedOpen && ( - <> - - Gas Price - {gasPrice} Gwei - - {/* TODO(shrugs): move to indigo/RangeInput */} - setGasPrice(parseInt(e.target.value, 10))} - /> - - Cheap - Fast + {error && ( + + {error.message} - - Nonce: {nonce} - - - Chain ID: {chainId} + )} + + {needFunds && ( + + The address {needFunds.address} needs at least{' '} + {fromWei(needFunds.minBalance)} ETH and currently has{' '} + {fromWei(needFunds.balance)} ETH. Waiting until the account has + enough funds. - - - )} - - )} + )} - {showSignedTx && ( - <> - - {signedTransactionOpen && ( - - {hexify(signedTransaction.serialize())} - - )} - - )} + {showConfigureInput && ( + <> + - {showReceipt && ( - <> - - - - Transaction Hash - - Etherscan↗ - - - - - {txHash} - - - - - - - )} + + + Gas Price + {gasPrice} Gwei + + {/* TODO(shrugs): move to indigo/RangeInput */} + setGasPrice(parseInt(e.target.value, 10))} + /> + + Cheap + Fast + + + Nonce: {nonce} + + + Chain ID: {chainId} + + + + + )} + + {showSignedTx && ( + <> + + + + {hexify(signedTransaction.serialize())} + + + + )} + + {showReceipt && ( + <> + + + + Transaction Hash + + Etherscan↗ + + + + + {txHash} + + + + + + + )} + + )} + ); } diff --git a/src/components/InputSigil.js b/src/components/InputSigil.js index 63223b711..3edcdf9ae 100644 --- a/src/components/InputSigil.js +++ b/src/components/InputSigil.js @@ -1,19 +1,18 @@ import React, { useEffect, useState } from 'react'; import { Just } from 'folktale/maybe'; -import * as ob from 'urbit-ob'; import MaybeSigil from './MaybeSigil'; -const selectColorway = (pass, fail, focused) => { - if (pass) { +const selectColorway = (valid, error, active) => { + if (valid) { return ['#2AA779', '#FFFFFF']; } - if (focused) { + if (active) { return ['#4330FC', '#FFFFFF']; } - if (fail) { + if (error) { return ['#F8C134', '#FFFFFF']; } @@ -24,25 +23,24 @@ export default function InputSigil({ className, patp, size, - pass, + valid, error, - focused, + active, ...rest }) { const [lastValidPatp, setLastValidPatp] = useState(patp); useEffect(() => { - if (ob.isValidPatp(patp)) { + if (valid) { setLastValidPatp(patp); } - }, [patp]); + }, [patp, valid]); return ( ); diff --git a/src/components/MaybeSigil.js b/src/components/MaybeSigil.js index 3a2ce8370..94f46f202 100644 --- a/src/components/MaybeSigil.js +++ b/src/components/MaybeSigil.js @@ -14,7 +14,7 @@ export default function MaybeSigil({ className, patp, size, ...rest }) { }); return validPatp ? ( - + ) : ( ); diff --git a/src/components/Passport.js b/src/components/Passport.js index 0ee827e3a..2fb379939 100644 --- a/src/components/Passport.js +++ b/src/components/Passport.js @@ -75,7 +75,7 @@ function Passport({ point, className, ticket = null, address = Nothing() }) { return ( - + {visualName} @@ -141,7 +141,6 @@ Passport.Mini = function MiniPassport({ point, className, inverted, ...rest }) { diff --git a/src/components/UploadButton.js b/src/components/UploadButton.js index 3d3b6607e..42f005415 100644 --- a/src/components/UploadButton.js +++ b/src/components/UploadButton.js @@ -6,7 +6,9 @@ export default function UploadButton({ children, onChange, ...rest }) { return (