diff options
Merge remote-tracking branch 'upstream/develop' into develop
Diffstat (limited to 'ui')
-rw-r--r-- | ui/app/actions.js | 136 | ||||
-rw-r--r-- | ui/app/app.js | 22 | ||||
-rw-r--r-- | ui/app/components/account-menu/index.js | 69 | ||||
-rw-r--r-- | ui/app/components/alert/index.js | 22 | ||||
-rw-r--r-- | ui/app/components/confirm-page-container/confirm-detail-row/confirm-detail-row.component.js | 18 | ||||
-rw-r--r-- | ui/app/components/customize-gas-modal/index.js | 6 | ||||
-rw-r--r-- | ui/app/components/dropdowns/account-dropdown-mini.js | 2 | ||||
-rw-r--r-- | ui/app/components/ens-input.js | 2 | ||||
-rw-r--r-- | ui/app/components/modals/confirm-remove-account/confirm-remove-account.component.js | 93 | ||||
-rw-r--r-- | ui/app/components/modals/confirm-remove-account/confirm-remove-account.container.js | 20 | ||||
-rw-r--r-- | ui/app/components/modals/confirm-remove-account/index.js | 2 | ||||
-rw-r--r-- | ui/app/components/modals/customize-gas/customize-gas.component.js | 2 | ||||
-rw-r--r-- | ui/app/components/modals/index.scss | 52 | ||||
-rw-r--r-- | ui/app/components/modals/modal.js | 14 | ||||
-rw-r--r-- | ui/app/components/network-display/index.scss | 6 | ||||
-rw-r--r-- | ui/app/components/pages/confirm-approve/confirm-approve.component.js | 19 | ||||
-rw-r--r-- | ui/app/components/pages/confirm-approve/confirm-approve.container.js | 19 | ||||
-rw-r--r-- | ui/app/components/pages/confirm-send-token/confirm-send-token.component.js | 20 | ||||
-rw-r--r-- | ui/app/components/pages/confirm-send-token/confirm-send-token.container.js | 26 | ||||
-rw-r--r-- | ui/app/components/pages/confirm-send-token/index.scss | 19 | ||||
-rw-r--r-- | ui/app/components/pages/confirm-token-transaction-base/confirm-token-transaction-base.component.js | 85 | ||||
-rw-r--r-- | ui/app/components/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js | 34 | ||||
-rw-r--r-- | ui/app/components/pages/confirm-token-transaction-base/index.js | 2 | ||||
-rw-r--r-- | ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js | 42 | ||||
-rw-r--r-- | ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js | 18 | ||||
-rw-r--r-- | ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js | 20 | ||||
-rw-r--r-- | ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.constants.js | 1 | ||||
-rw-r--r-- | ui/app/components/pages/confirm-transaction/confirm-transaction.component.js | 7 | ||||
-rw-r--r-- | ui/app/components/pages/create-account/connect-hardware/account-list.js | 143 | ||||
-rw-r--r-- | ui/app/components/pages/create-account/connect-hardware/connect-screen.js | 149 | ||||
-rw-r--r-- | ui/app/components/pages/create-account/connect-hardware/index.js | 234 | ||||
-rw-r--r-- | ui/app/components/pages/create-account/index.js | 25 | ||||
-rw-r--r-- | ui/app/components/pages/create-account/new-account.js | 2 | ||||
-rw-r--r-- | ui/app/components/pages/index.scss | 2 | ||||
-rw-r--r-- | ui/app/components/pending-tx/confirm-deploy-contract.js | 358 | ||||
-rw-r--r-- | ui/app/components/pending-tx/confirm-send-ether.js | 692 | ||||
-rw-r--r-- | ui/app/components/pending-tx/confirm-send-token.js | 696 | ||||
-rw-r--r-- | ui/app/components/pending-tx/index.js | 165 | ||||
-rw-r--r-- | ui/app/components/selected-account/selected-account.component.js | 11 | ||||
-rw-r--r-- | ui/app/components/send/README.md (renamed from ui/app/components/send_/README.md) | 0 | ||||
-rw-r--r-- | ui/app/components/send/account-list-item/account-list-item-README.md (renamed from ui/app/components/send_/account-list-item/account-list-item-README.md) | 0 | ||||
-rw-r--r-- | ui/app/components/send/account-list-item/account-list-item.component.js (renamed from ui/app/components/send_/account-list-item/account-list-item.component.js) | 2 | ||||
-rw-r--r-- | ui/app/components/send/account-list-item/account-list-item.container.js (renamed from ui/app/components/send_/account-list-item/account-list-item.container.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/account-list-item/account-list-item.scss (renamed from ui/app/components/send_/account-list-item/account-list-item.scss) | 0 | ||||
-rw-r--r-- | ui/app/components/send/account-list-item/index.js (renamed from ui/app/components/send_/account-list-item/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/account-list-item/tests/account-list-item-component.test.js (renamed from ui/app/components/send_/account-list-item/tests/account-list-item-component.test.js) | 2 | ||||
-rw-r--r-- | ui/app/components/send/account-list-item/tests/account-list-item-container.test.js (renamed from ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/currency-display/currency-display.js (renamed from ui/app/components/send/currency-display.js) | 43 | ||||
-rw-r--r-- | ui/app/components/send/currency-display/index.js | 1 | ||||
-rw-r--r-- | ui/app/components/send/index.js (renamed from ui/app/components/send_/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/index.js (renamed from ui/app/components/send_/send-content/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/README.md (renamed from ui/app/components/send_/send-content/send-amount-row/README.md) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/amount-max-button/amount-max-button.component.js (renamed from ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.component.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js (renamed from ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.container.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/amount-max-button/amount-max-button.selectors.js (renamed from ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.selectors.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/amount-max-button/amount-max-button.utils.js (renamed from ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.utils.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/amount-max-button/index.js (renamed from ui/app/components/send_/send-content/send-amount-row/amount-max-button/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js (renamed from ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js (renamed from ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-selectors.test.js (renamed from ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-selectors.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-utils.test.js (renamed from ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-utils.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/index.js (renamed from ui/app/components/send_/send-content/send-amount-row/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/send-amount-row.component.js (renamed from ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js) | 2 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/send-amount-row.container.js (renamed from ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/send-amount-row.scss (renamed from ui/app/components/send_/send-content/send-amount-row/send-amount-row.scss) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/send-amount-row.selectors.js (renamed from ui/app/components/send_/send-content/send-amount-row/send-amount-row.selectors.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-component.test.js (renamed from ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js) | 2 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-container.test.js (renamed from ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-selectors.test.js (renamed from ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-selectors.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-content-README.md (renamed from ui/app/components/send_/send-content/send-content-README.md) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-content.component.js (renamed from ui/app/components/send_/send-content/send-content.component.js) | 2 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-content.scss (renamed from ui/app/components/send_/send-content/send-content.scss) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-dropdown-list/index.js (renamed from ui/app/components/send_/send-content/send-dropdown-list/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-dropdown-list/send-dropdown-list.component.js (renamed from ui/app/components/send_/send-content/send-dropdown-list/send-dropdown-list.component.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-dropdown-list/tests/send-dropdown-list-component.test.js (renamed from ui/app/components/send_/send-content/send-dropdown-list/tests/send-dropdown-list-component.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-from-row/from-dropdown/from-dropdown-README.md (renamed from ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown-README.md) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-from-row/from-dropdown/from-dropdown.component.js (renamed from ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown.component.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-from-row/from-dropdown/from-dropdown.scss (renamed from ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown.scss) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-from-row/from-dropdown/index.js (renamed from ui/app/components/send_/send-content/send-from-row/from-dropdown/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-from-row/from-dropdown/tests/from-dropdown-component.test.js (renamed from ui/app/components/send_/send-content/send-from-row/from-dropdown/tests/from-dropdown-component.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-from-row/index.js (renamed from ui/app/components/send_/send-content/send-from-row/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-from-row/send-from-row-README.md (renamed from ui/app/components/send_/send-content/send-from-row/send-from-row-README.md) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-from-row/send-from-row.component.js (renamed from ui/app/components/send_/send-content/send-from-row/send-from-row.component.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-from-row/send-from-row.container.js (renamed from ui/app/components/send_/send-content/send-from-row/send-from-row.container.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-from-row/send-from-row.selectors.js (renamed from ui/app/components/send_/send-content/send-from-row/send-from-row.selectors.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-from-row/tests/send-from-row-component.test.js (renamed from ui/app/components/send_/send-content/send-from-row/tests/send-from-row-component.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-from-row/tests/send-from-row-container.test.js (renamed from ui/app/components/send_/send-content/send-from-row/tests/send-from-row-container.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-from-row/tests/send-from-row-selectors.test.js (renamed from ui/app/components/send_/send-content/send-from-row/tests/send-from-row-selectors.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-gas-row/README.md (renamed from ui/app/components/send_/send-content/send-gas-row/README.md) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js (renamed from ui/app/components/send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-gas-row/gas-fee-display/index.js (renamed from ui/app/components/send_/send-content/send-gas-row/gas-fee-display/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js (renamed from ui/app/components/send_/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-gas-row/index.js (renamed from ui/app/components/send_/send-content/send-gas-row/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-gas-row/send-gas-row.component.js (renamed from ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-gas-row/send-gas-row.container.js (renamed from ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-gas-row/send-gas-row.scss (renamed from ui/app/components/send_/send-content/send-gas-row/send-gas-row.scss) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-gas-row/send-gas-row.selectors.js (renamed from ui/app/components/send_/send-content/send-gas-row/send-gas-row.selectors.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-component.test.js (renamed from ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-container.test.js (renamed from ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-selectors.test.js (renamed from ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-selectors.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-hex-data-row/index.js | 1 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-hex-data-row/send-hex-data-row.component.js | 40 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-hex-data-row/send-hex-data-row.container.js | 21 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-row-wrapper/index.js (renamed from ui/app/components/send_/send-content/send-row-wrapper/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/index.js (renamed from ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message-README.md (renamed from ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message-README.md) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js (renamed from ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.container.js (renamed from ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.container.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.scss (renamed from ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.scss) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/tests/send-row-error-message-component.test.js (renamed from ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/tests/send-row-error-message-component.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/tests/send-row-error-message-container.test.js (renamed from ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/tests/send-row-error-message-container.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-row-wrapper/send-row-wrapper-README.md (renamed from ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper-README.md) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-row-wrapper/send-row-wrapper.component.js (renamed from ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper.component.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-row-wrapper/send-row-wrapper.scss (renamed from ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper.scss) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-row-wrapper/tests/send-row-wrapper-component.test.js (renamed from ui/app/components/send_/send-content/send-row-wrapper/tests/send-row-wrapper-component.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-to-row/index.js (renamed from ui/app/components/send_/send-content/send-to-row/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-to-row/send-to-row-README.md (renamed from ui/app/components/send_/send-content/send-to-row/send-to-row-README.md) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-to-row/send-to-row.component.js (renamed from ui/app/components/send_/send-content/send-to-row/send-to-row.component.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-to-row/send-to-row.container.js (renamed from ui/app/components/send_/send-content/send-to-row/send-to-row.container.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-to-row/send-to-row.selectors.js (renamed from ui/app/components/send_/send-content/send-to-row/send-to-row.selectors.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-to-row/send-to-row.utils.js (renamed from ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-to-row/tests/send-to-row-component.test.js (renamed from ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-to-row/tests/send-to-row-container.test.js (renamed from ui/app/components/send_/send-content/send-to-row/tests/send-to-row-container.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-to-row/tests/send-to-row-selectors.test.js (renamed from ui/app/components/send_/send-content/send-to-row/tests/send-to-row-selectors.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-to-row/tests/send-to-row-utils.test.js (renamed from ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-content/tests/send-content-component.test.js (renamed from ui/app/components/send_/send-content/tests/send-content-component.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-footer/README.md (renamed from ui/app/components/send_/send-footer/README.md) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-footer/index.js (renamed from ui/app/components/send_/send-footer/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-footer/send-footer.component.js (renamed from ui/app/components/send_/send-footer/send-footer.component.js) | 5 | ||||
-rw-r--r-- | ui/app/components/send/send-footer/send-footer.container.js (renamed from ui/app/components/send_/send-footer/send-footer.container.js) | 7 | ||||
-rw-r--r-- | ui/app/components/send/send-footer/send-footer.scss (renamed from ui/app/components/send_/send-footer/send-footer.scss) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-footer/send-footer.selectors.js (renamed from ui/app/components/send_/send-footer/send-footer.selectors.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-footer/send-footer.utils.js (renamed from ui/app/components/send_/send-footer/send-footer.utils.js) | 38 | ||||
-rw-r--r-- | ui/app/components/send/send-footer/tests/send-footer-component.test.js (renamed from ui/app/components/send_/send-footer/tests/send-footer-component.test.js) | 2 | ||||
-rw-r--r-- | ui/app/components/send/send-footer/tests/send-footer-container.test.js (renamed from ui/app/components/send_/send-footer/tests/send-footer-container.test.js) | 5 | ||||
-rw-r--r-- | ui/app/components/send/send-footer/tests/send-footer-selectors.test.js (renamed from ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-footer/tests/send-footer-utils.test.js (renamed from ui/app/components/send_/send-footer/tests/send-footer-utils.test.js) | 24 | ||||
-rw-r--r-- | ui/app/components/send/send-header/README.md (renamed from ui/app/components/send_/send-header/README.md) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-header/index.js (renamed from ui/app/components/send_/send-header/index.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-header/send-header.component.js (renamed from ui/app/components/send_/send-header/send-header.component.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-header/send-header.container.js (renamed from ui/app/components/send_/send-header/send-header.container.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-header/send-header.selectors.js (renamed from ui/app/components/send_/send-header/send-header.selectors.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-header/tests/send-header-component.test.js (renamed from ui/app/components/send_/send-header/tests/send-header-component.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-header/tests/send-header-container.test.js (renamed from ui/app/components/send_/send-header/tests/send-header-container.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send-header/tests/send-header-selectors.test.js (renamed from ui/app/components/send_/send-header/tests/send-header-selectors.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send.component.js (renamed from ui/app/components/send_/send.component.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send.constants.js (renamed from ui/app/components/send_/send.constants.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send.container.js (renamed from ui/app/components/send_/send.container.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send.scss (renamed from ui/app/components/send_/send.scss) | 0 | ||||
-rw-r--r-- | ui/app/components/send/send.selectors.js (renamed from ui/app/components/send_/send.selectors.js) | 5 | ||||
-rw-r--r-- | ui/app/components/send/send.utils.js (renamed from ui/app/components/send_/send.utils.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/tests/send-component.test.js (renamed from ui/app/components/send_/tests/send-component.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/tests/send-container.test.js (renamed from ui/app/components/send_/tests/send-container.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/tests/send-selectors-test-data.js (renamed from ui/app/components/send_/tests/send-selectors-test-data.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/tests/send-selectors.test.js (renamed from ui/app/components/send_/tests/send-selectors.test.js) | 0 | ||||
-rw-r--r-- | ui/app/components/send/tests/send-utils.test.js (renamed from ui/app/components/send_/tests/send-utils.test.js) | 26 | ||||
-rw-r--r-- | ui/app/components/send/to-autocomplete.component.js | 2 | ||||
-rw-r--r-- | ui/app/components/send/to-autocomplete/index.js | 1 | ||||
-rw-r--r-- | ui/app/components/send/to-autocomplete/to-autocomplete.js | 120 | ||||
-rw-r--r-- | ui/app/components/send_/send.utils.test.js | 30 | ||||
-rw-r--r-- | ui/app/components/tx-list-item.js | 24 | ||||
-rw-r--r-- | ui/app/components/wallet-view.js | 2 | ||||
-rw-r--r-- | ui/app/conf-tx.js | 136 | ||||
-rw-r--r-- | ui/app/css/itcss/components/account-menu.scss | 18 | ||||
-rw-r--r-- | ui/app/css/itcss/components/alert.scss | 57 | ||||
-rw-r--r-- | ui/app/css/itcss/components/index.scss | 2 | ||||
-rw-r--r-- | ui/app/css/itcss/components/new-account.scss | 297 | ||||
-rw-r--r-- | ui/app/css/itcss/components/send.scss | 4 | ||||
-rw-r--r-- | ui/app/css/itcss/components/transaction-list.scss | 10 | ||||
-rw-r--r-- | ui/app/helpers/confirm-transaction/util.js | 17 | ||||
-rw-r--r-- | ui/app/reducers/app.js | 15 | ||||
-rw-r--r-- | ui/app/reducers/metamask.js | 8 | ||||
-rw-r--r-- | ui/app/routes.js | 4 | ||||
-rw-r--r-- | ui/app/selectors/confirm-transaction.js | 99 | ||||
-rw-r--r-- | ui/app/util.js | 9 |
175 files changed, 2058 insertions, 2280 deletions
diff --git a/ui/app/actions.js b/ui/app/actions.js index 1fb49c920..6c947fc35 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -6,7 +6,7 @@ const { calcGasTotal, calcTokenBalance, estimateGas, -} = require('./components/send_/send.utils') +} = require('./components/send/send.utils') const ethUtil = require('ethereumjs-util') const { fetchLocale } = require('../i18n-helper') const log = require('loglevel') @@ -26,6 +26,11 @@ var actions = { SIDEBAR_CLOSE: 'UI_SIDEBAR_CLOSE', showSidebar: showSidebar, hideSidebar: hideSidebar, + // sidebar state + ALERT_OPEN: 'UI_ALERT_OPEN', + ALERT_CLOSE: 'UI_ALERT_CLOSE', + showAlert: showAlert, + hideAlert: hideAlert, // network dropdown open NETWORK_DROPDOWN_OPEN: 'UI_NETWORK_DROPDOWN_OPEN', NETWORK_DROPDOWN_CLOSE: 'UI_NETWORK_DROPDOWN_CLOSE', @@ -78,9 +83,14 @@ var actions = { addNewKeyring, importNewAccount, addNewAccount, + connectHardware, + checkHardwareStatus, + forgetDevice, + unlockTrezorAccount, NEW_ACCOUNT_SCREEN: 'NEW_ACCOUNT_SCREEN', navigateToNewAccountScreen, resetAccount, + removeAccount, showNewVaultSeed: showNewVaultSeed, showInfoPage: showInfoPage, CLOSE_WELCOME_SCREEN: 'CLOSE_WELCOME_SCREEN', @@ -164,6 +174,7 @@ var actions = { UPDATE_GAS_PRICE: 'UPDATE_GAS_PRICE', UPDATE_GAS_TOTAL: 'UPDATE_GAS_TOTAL', UPDATE_SEND_FROM: 'UPDATE_SEND_FROM', + UPDATE_SEND_HEX_DATA: 'UPDATE_SEND_HEX_DATA', UPDATE_SEND_TOKEN_BALANCE: 'UPDATE_SEND_TOKEN_BALANCE', UPDATE_SEND_TO: 'UPDATE_SEND_TO', UPDATE_SEND_AMOUNT: 'UPDATE_SEND_AMOUNT', @@ -183,6 +194,7 @@ var actions = { setSendTokenBalance, updateSendTokenBalance, updateSendFrom, + updateSendHexData, updateSendTo, updateSendAmount, updateSendMemo, @@ -533,6 +545,26 @@ function resetAccount () { } } +function removeAccount (address) { + return dispatch => { + dispatch(actions.showLoadingIndication()) + + return new Promise((resolve, reject) => { + background.removeAccount(address, (err, account) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + log.info('Account removed: ' + account) + dispatch(actions.showAccountsPage()) + resolve() + }) + }) + } +} + function addNewKeyring (type, opts) { return (dispatch) => { dispatch(actions.showLoadingIndication()) @@ -599,6 +631,88 @@ function addNewAccount () { } } +function checkHardwareStatus (deviceName) { + log.debug(`background.checkHardwareStatus`, deviceName) + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.checkHardwareStatus(deviceName, (err, unlocked) => { + if (err) { + log.error(err) + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.hideLoadingIndication()) + + forceUpdateMetamaskState(dispatch) + return resolve(unlocked) + }) + }) + } +} + +function forgetDevice (deviceName) { + log.debug(`background.forgetDevice`, deviceName) + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.forgetDevice(deviceName, (err, response) => { + if (err) { + log.error(err) + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.hideLoadingIndication()) + + forceUpdateMetamaskState(dispatch) + return resolve() + }) + }) + } +} + +function connectHardware (deviceName, page) { + log.debug(`background.connectHardware`, deviceName, page) + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.connectHardware(deviceName, page, (err, accounts) => { + if (err) { + log.error(err) + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.hideLoadingIndication()) + + forceUpdateMetamaskState(dispatch) + return resolve(accounts) + }) + }) + } +} + +function unlockTrezorAccount (index) { + log.debug(`background.unlockTrezorAccount`, index) + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.unlockTrezorAccount(index, (err, accounts) => { + if (err) { + log.error(err) + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.hideLoadingIndication()) + return resolve() + }) + }) + } +} + function showInfoPage () { return { type: actions.SHOW_INFO_PAGE, @@ -838,6 +952,13 @@ function updateSendFrom (from) { } } +function updateSendHexData (value) { + return { + type: actions.UPDATE_SEND_HEX_DATA, + value, + } +} + function updateSendTo (to, nickname = '') { return { type: actions.UPDATE_SEND_TO, @@ -1617,6 +1738,19 @@ function hideSidebar () { } } +function showAlert (msg) { + return { + type: actions.ALERT_OPEN, + value: msg, + } +} + +function hideAlert () { + return { + type: actions.ALERT_CLOSE, + } +} + function showLoadingIndication (message) { return { diff --git a/ui/app/app.js b/ui/app/app.js index 74d360d3c..dbb6146d1 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -11,7 +11,7 @@ const log = require('loglevel') // init const InitializeScreen = require('../../mascara/src/app/first-time').default // accounts -const SendTransactionScreen = require('./components/send_/send.container') +const SendTransactionScreen = require('./components/send/send.container') const ConfirmTransaction = require('./components/pages/confirm-transaction') // slideout menu @@ -36,6 +36,8 @@ const AccountMenu = require('./components/account-menu') // Global Modals const Modal = require('./components/modals/index').Modal +// Global Alert +const Alert = require('./components/alert') const AppHeader = require('./components/app-header') @@ -93,6 +95,7 @@ class App extends Component { render () { const { isLoading, + alertMessage, loadingMessage, network, isMouseUser, @@ -126,6 +129,9 @@ class App extends Component { // global modal h(Modal, {}, []), + // global alert + h(Alert, {visible: this.props.alertOpen, msg: alertMessage}), + h(AppHeader), // sidebar @@ -149,14 +155,6 @@ class App extends Component { ) } - renderGlobalModal () { - return h(Modal, { - ref: 'modalRef', - }, [ - // h(BuyOptions, {}, []), - ]) - } - renderSidebar () { return h('div', [ h('style', ` @@ -265,11 +263,13 @@ App.propTypes = { setCurrentCurrencyToUSD: PropTypes.func, isLoading: PropTypes.bool, loadingMessage: PropTypes.string, + alertMessage: PropTypes.string, network: PropTypes.string, provider: PropTypes.object, frequentRpcList: PropTypes.array, currentView: PropTypes.object, sidebarOpen: PropTypes.bool, + alertOpen: PropTypes.bool, hideSidebar: PropTypes.func, isMascara: PropTypes.bool, isOnboarding: PropTypes.bool, @@ -305,6 +305,8 @@ function mapStateToProps (state) { const { networkDropdownOpen, sidebarOpen, + alertOpen, + alertMessage, isLoading, loadingMessage, } = appState @@ -330,6 +332,8 @@ function mapStateToProps (state) { // state from plugin networkDropdownOpen, sidebarOpen, + alertOpen, + alertMessage, isLoading, loadingMessage, noActiveNotices, diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js index f34631ca8..9c063d31e 100644 --- a/ui/app/components/account-menu/index.js +++ b/ui/app/components/account-menu/index.js @@ -9,11 +9,17 @@ const actions = require('../../actions') const { Menu, Item, Divider, CloseArea } = require('../dropdowns/components/menu') const Identicon = require('../identicon') const { formatBalance } = require('../../util') +const { ENVIRONMENT_TYPE_POPUP } = require('../../../../app/scripts/lib/enums') +const { getEnvironmentType } = require('../../../../app/scripts/lib/util') +const Tooltip = require('../tooltip') + + const { SETTINGS_ROUTE, INFO_ROUTE, NEW_ACCOUNT_ROUTE, IMPORT_ACCOUNT_ROUTE, + CONNECT_HARDWARE_ROUTE, DEFAULT_ROUTE, } = require('../../routes') @@ -63,6 +69,9 @@ function mapDispatchToProps (dispatch) { dispatch(actions.hideSidebar()) dispatch(actions.toggleAccountMenu()) }, + showRemoveAccountConfirmationModal: (identity) => { + return dispatch(actions.showModal({ name: 'CONFIRM_REMOVE_ACCOUNT', identity })) + }, } } @@ -106,6 +115,18 @@ AccountMenu.prototype.render = function () { icon: h('img.account-menu__item-icon', { src: 'images/import-account.svg' }), text: this.context.t('importAccount'), }), + h(Item, { + onClick: () => { + toggleAccountMenu() + if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP) { + global.platform.openExtensionInBrowser(CONNECT_HARDWARE_ROUTE) + } else { + history.push(CONNECT_HARDWARE_ROUTE) + } + }, + icon: h('img.account-menu__item-icon', { src: 'images/connect-icon.svg' }), + text: this.context.t('connectHardwareWallet'), + }), h(Divider), h(Item, { onClick: () => { @@ -136,7 +157,8 @@ AccountMenu.prototype.renderAccounts = function () { } = this.props const accountOrder = keyrings.reduce((list, keyring) => list.concat(keyring.accounts), []) - return accountOrder.map((address) => { + return accountOrder.filter(address => !!identities[address]).map((address) => { + const identity = identities[address] const isSelected = identity.address === selectedAddress @@ -170,16 +192,53 @@ AccountMenu.prototype.renderAccounts = function () { h('div.account-menu__balance', formattedBalance), ]), - this.indicateIfLoose(keyring), + this.renderKeyringType(keyring), + this.renderRemoveAccount(keyring, identity), ], ) }) } -AccountMenu.prototype.indicateIfLoose = function (keyring) { +AccountMenu.prototype.renderRemoveAccount = function (keyring, identity) { + // Any account that's not from the HD wallet Keyring can be removed + const type = keyring.type + const isRemovable = type !== 'HD Key Tree' + if (isRemovable) { + return h(Tooltip, { + title: this.context.t('removeAccount'), + position: 'bottom', + }, [ + h('a.remove-account-icon', { + onClick: (e) => this.removeAccount(e, identity), + }, ''), + ]) + } + return null +} + +AccountMenu.prototype.removeAccount = function (e, identity) { + e.preventDefault() + e.stopPropagation() + const { showRemoveAccountConfirmationModal } = this.props + showRemoveAccountConfirmationModal(identity) +} + +AccountMenu.prototype.renderKeyringType = function (keyring) { try { // Sometimes keyrings aren't loaded yet: const type = keyring.type - const isLoose = type !== 'HD Key Tree' - return isLoose ? h('.keyring-label.allcaps', this.context.t('imported')) : null + let label + switch (type) { + case 'Trezor Hardware': + label = this.context.t('hardware') + break + case 'Simple Key Pair': + label = this.context.t('imported') + break + default: + label = '' + } + + return label !== '' ? h('.keyring-label.allcaps', label) : null + } catch (e) { return } } diff --git a/ui/app/components/alert/index.js b/ui/app/components/alert/index.js new file mode 100644 index 000000000..fc39d41e2 --- /dev/null +++ b/ui/app/components/alert/index.js @@ -0,0 +1,22 @@ +const { Component } = require('react') +const PropTypes = require('prop-types') +const h = require('react-hyperscript') + +class Alert extends Component { + + render () { + const className = `.global-alert${this.props.visible ? '.visible' : '.hidden'}` + return ( + h(`div${className}`, {}, + h('a.msg', {}, this.props.msg) + ) + ) + } +} + +Alert.propTypes = { + visible: PropTypes.bool.isRequired, + msg: PropTypes.string, +} +module.exports = Alert + diff --git a/ui/app/components/confirm-page-container/confirm-detail-row/confirm-detail-row.component.js b/ui/app/components/confirm-page-container/confirm-detail-row/confirm-detail-row.component.js index 631cf5803..f0703dde2 100644 --- a/ui/app/components/confirm-page-container/confirm-detail-row/confirm-detail-row.component.js +++ b/ui/app/components/confirm-page-container/confirm-detail-row/confirm-detail-row.component.js @@ -5,10 +5,10 @@ import classnames from 'classnames' const ConfirmDetailRow = props => { const { label, - fiatFee, - ethFee, + fiatText, + ethText, onHeaderClick, - fiatFeeColor, + fiatTextColor, headerText, headerTextClassName, } = props @@ -27,12 +27,12 @@ const ConfirmDetailRow = props => { </div> <div className="confirm-detail-row__fiat" - style={{ color: fiatFeeColor }} + style={{ color: fiatTextColor }} > - { fiatFee } + { fiatText } </div> <div className="confirm-detail-row__eth"> - { `\u2666 ${ethFee}` } + { ethText } </div> </div> </div> @@ -41,9 +41,9 @@ const ConfirmDetailRow = props => { ConfirmDetailRow.propTypes = { label: PropTypes.string, - fiatFee: PropTypes.string, - ethFee: PropTypes.string, - fiatFeeColor: PropTypes.string, + fiatText: PropTypes.string, + ethText: PropTypes.string, + fiatTextColor: PropTypes.string, onHeaderClick: PropTypes.func, headerText: PropTypes.string, headerTextClassName: PropTypes.string, diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js index cefa428b9..c255fd64d 100644 --- a/ui/app/components/customize-gas-modal/index.js +++ b/ui/app/components/customize-gas-modal/index.js @@ -16,11 +16,11 @@ const { MIN_GAS_PRICE_DEC, MIN_GAS_LIMIT_DEC, MIN_GAS_PRICE_GWEI, -} = require('../send_/send.constants') +} = require('../send/send.constants') const { isBalanceSufficient, -} = require('../send_/send.utils') +} = require('../send/send.utils') const { conversionUtil, @@ -45,7 +45,7 @@ const { const { getGasPrice, getGasLimit, -} = require('../send_/send.selectors') +} = require('../send/send.selectors') function mapStateToProps (state) { const selectedToken = getSelectedToken(state) diff --git a/ui/app/components/dropdowns/account-dropdown-mini.js b/ui/app/components/dropdowns/account-dropdown-mini.js index a7a908d3b..261eb0aa2 100644 --- a/ui/app/components/dropdowns/account-dropdown-mini.js +++ b/ui/app/components/dropdowns/account-dropdown-mini.js @@ -1,7 +1,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits -const AccountListItem = require('../send_/account-list-item/account-list-item.component').default +const AccountListItem = require('../send/account-list-item/account-list-item.component').default module.exports = AccountDropdownMini diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js index adbf2dba8..b9f99b3d1 100644 --- a/ui/app/components/ens-input.js +++ b/ui/app/components/ens-input.js @@ -10,7 +10,7 @@ const networkMap = require('ethjs-ens/lib/network-map.json') const ensRE = /.+\..+$/ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' const connect = require('react-redux').connect -const ToAutoComplete = require('./send/to-autocomplete.component').default +const ToAutoComplete = require('./send/to-autocomplete').default const log = require('loglevel') const { isValidENSAddress } = require('../util') diff --git a/ui/app/components/modals/confirm-remove-account/confirm-remove-account.component.js b/ui/app/components/modals/confirm-remove-account/confirm-remove-account.component.js new file mode 100644 index 000000000..5a9f0f289 --- /dev/null +++ b/ui/app/components/modals/confirm-remove-account/confirm-remove-account.component.js @@ -0,0 +1,93 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import Button from '../../button' +import { addressSummary } from '../../../util' +import Identicon from '../../identicon' +import genAccountLink from '../../../../lib/account-link' + +class ConfirmRemoveAccount extends Component { + static propTypes = { + hideModal: PropTypes.func.isRequired, + removeAccount: PropTypes.func.isRequired, + identity: PropTypes.object.isRequired, + network: PropTypes.string.isRequired, + } + + static contextTypes = { + t: PropTypes.func, + } + + handleRemove () { + this.props.removeAccount(this.props.identity.address) + .then(() => this.props.hideModal()) + } + + renderSelectedAccount () { + const { identity } = this.props + return ( + <div className="modal-container__account"> + <div className="modal-container__account__identicon"> + <Identicon + address={identity.address} + diameter={32} + /> + </div> + <div className="modal-container__account__name"> + <span className="modal-container__account__label">Name</span> + <span className="account_value">{identity.name}</span> + </div> + <div className="modal-container__account__address"> + <span className="modal-container__account__label">Public Address</span> + <span className="account_value">{ addressSummary(identity.address, 4, 4) }</span> + </div> + <div className="modal-container__account__link"> + <a + className="" + href={genAccountLink(identity.address, this.props.network)} + target={'_blank'} + title={this.context.t('etherscanView')} + > + <img src="images/popout.svg" /> + </a> + </div> + </div> + ) + } + + render () { + const { t } = this.context + + return ( + <div className="modal-container"> + <div className="modal-container__content"> + <div className="modal-container__title"> + { `${t('removeAccount')}` }? + </div> + { this.renderSelectedAccount() } + <div className="modal-container__description"> + { t('removeAccountDescription') } + <a className="modal-container__link" rel="noopener noreferrer" target="_blank" href="https://consensys.zendesk.com/hc/en-us/articles/360004180111-What-are-imported-accounts-New-UI-">{ t('learnMore') }</a> + </div> + </div> + <div className="modal-container__footer"> + <Button + type="default" + className="modal-container__footer-button" + onClick={() => this.props.hideModal()} + > + { t('nevermind') } + </Button> + <Button + type="secondary" + className="modal-container__footer-button" + onClick={() => this.handleRemove()} + > + { t('remove') } + </Button> + </div> + </div> + ) + } +} + +export default ConfirmRemoveAccount diff --git a/ui/app/components/modals/confirm-remove-account/confirm-remove-account.container.js b/ui/app/components/modals/confirm-remove-account/confirm-remove-account.container.js new file mode 100644 index 000000000..4b194c995 --- /dev/null +++ b/ui/app/components/modals/confirm-remove-account/confirm-remove-account.container.js @@ -0,0 +1,20 @@ +import { connect } from 'react-redux' +import ConfirmRemoveAccount from './confirm-remove-account.component' + +const { hideModal, removeAccount } = require('../../../actions') + +const mapStateToProps = state => { + return { + identity: state.appState.modal.modalState.props.identity, + network: state.metamask.network, + } +} + +const mapDispatchToProps = dispatch => { + return { + hideModal: () => dispatch(hideModal()), + removeAccount: (address) => dispatch(removeAccount(address)), + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(ConfirmRemoveAccount) diff --git a/ui/app/components/modals/confirm-remove-account/index.js b/ui/app/components/modals/confirm-remove-account/index.js new file mode 100644 index 000000000..9763fbe05 --- /dev/null +++ b/ui/app/components/modals/confirm-remove-account/index.js @@ -0,0 +1,2 @@ +import ConfirmRemoveAccount from './confirm-remove-account.container' +module.exports = ConfirmRemoveAccount diff --git a/ui/app/components/modals/customize-gas/customize-gas.component.js b/ui/app/components/modals/customize-gas/customize-gas.component.js index d17c290b6..0337c5413 100644 --- a/ui/app/components/modals/customize-gas/customize-gas.component.js +++ b/ui/app/components/modals/customize-gas/customize-gas.component.js @@ -1,7 +1,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import GasModalCard from '../../customize-gas-modal/gas-modal-card' -import { MIN_GAS_PRICE_GWEI } from '../../send_/send.constants' +import { MIN_GAS_PRICE_GWEI } from '../../send/send.constants' import { getDecimalGasLimit, diff --git a/ui/app/components/modals/index.scss b/ui/app/components/modals/index.scss index 160911c10..e198cca44 100644 --- a/ui/app/components/modals/index.scss +++ b/ui/app/components/modals/index.scss @@ -20,6 +20,58 @@ font-size: .875rem; } + &__account { + border: 1px solid #b7b7b7; + border-radius: 4px; + padding: 10px; + display: flex; + margin-top: 10px; + margin-bottom: 20px; + width: 100%; + + &__identicon { + margin-right: 10px; + } + + &__name, + &__address { + margin-right: 10px; + font-size: 14px; + } + + &__name { + width: 100px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + &__label { + font-size: 11px; + display: block; + color: #9b9b9b; + } + + &__link { + margin-top: 14px; + + img { + width: 15px; + height: 15px; + } + } + + @media screen and (max-width: 575px) { + &__name { + width: 90px; + } + } + } + + &__link { + color: #2f9ae0; + } + &__content { overflow-y: auto; flex: 1; diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js index 973438b6b..f59825ed1 100644 --- a/ui/app/components/modals/modal.js +++ b/ui/app/components/modals/modal.js @@ -20,6 +20,7 @@ const HideTokenConfirmationModal = require('./hide-token-confirmation-modal') const CustomizeGasModal = require('../customize-gas-modal') const NotifcationModal = require('./notification-modal') const ConfirmResetAccount = require('./confirm-reset-account') +const ConfirmRemoveAccount = require('./confirm-remove-account') const TransactionConfirmed = require('./transaction-confirmed') const WelcomeBeta = require('./welcome-beta') const Notification = require('./notification') @@ -243,6 +244,19 @@ const MODALS = { }, }, + CONFIRM_REMOVE_ACCOUNT: { + contents: h(ConfirmRemoveAccount), + mobileModalStyle: { + ...modalContainerMobileStyle, + }, + laptopModalStyle: { + ...modalContainerLaptopStyle, + }, + contentStyle: { + borderRadius: '8px', + }, + }, + NEW_ACCOUNT: { contents: [ h(NewAccountModal, {}, []), diff --git a/ui/app/components/network-display/index.scss b/ui/app/components/network-display/index.scss index e82d0e70c..2085cff67 100644 --- a/ui/app/components/network-display/index.scss +++ b/ui/app/components/network-display/index.scss @@ -9,7 +9,7 @@ height: 25px; &--mainnet { - background-color: lighten($blue-lagoon, 45%); + background-color: lighten($blue-lagoon, 68%); } &--ropsten { @@ -17,11 +17,11 @@ } &--kovan { - background-color: lighten($purple, 45%); + background-color: lighten($purple, 65%); } &--rinkeby { - background-color: lighten($tulip-tree, 45%); + background-color: lighten($tulip-tree, 35%); } } diff --git a/ui/app/components/pages/confirm-approve/confirm-approve.component.js b/ui/app/components/pages/confirm-approve/confirm-approve.component.js index d775b0362..b71eaa1d4 100644 --- a/ui/app/components/pages/confirm-approve/confirm-approve.component.js +++ b/ui/app/components/pages/confirm-approve/confirm-approve.component.js @@ -1,29 +1,20 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import ConfirmTransactionBase from '../confirm-transaction-base' +import ConfirmTokenTransactionBase from '../confirm-token-transaction-base' export default class ConfirmApprove extends Component { - static contextTypes = { - t: PropTypes.func, - } - static propTypes = { - tokenAddress: PropTypes.string, - toAddress: PropTypes.string, - tokenAmount: PropTypes.string, + tokenAmount: PropTypes.number, tokenSymbol: PropTypes.string, } render () { - const { toAddress, tokenAddress, tokenAmount, tokenSymbol } = this.props + const { tokenAmount, tokenSymbol } = this.props return ( - <ConfirmTransactionBase - toAddress={toAddress} - identiconAddress={tokenAddress} - title={`${tokenAmount} ${tokenSymbol}`} + <ConfirmTokenTransactionBase + tokenAmount={tokenAmount} warning={`By approving this action, you grant permission for this contract to spend up to ${tokenAmount} of your ${tokenSymbol}.`} - hideSubtitle /> ) } diff --git a/ui/app/components/pages/confirm-approve/confirm-approve.container.js b/ui/app/components/pages/confirm-approve/confirm-approve.container.js index 040e499ae..4ef9f4ced 100644 --- a/ui/app/components/pages/confirm-approve/confirm-approve.container.js +++ b/ui/app/components/pages/confirm-approve/confirm-approve.container.js @@ -1,25 +1,12 @@ import { connect } from 'react-redux' import ConfirmApprove from './confirm-approve.component' +import { approveTokenAmountAndToAddressSelector } from '../../../selectors/confirm-transaction' const mapStateToProps = state => { - const { confirmTransaction } = state - const { - tokenData = {}, - txData: { txParams: { to: tokenAddress } = {} } = {}, - tokenProps: { tokenSymbol } = {}, - } = confirmTransaction - const { params = [] } = tokenData - - let toAddress = '' - let tokenAmount = '' - - if (params && params.length === 2) { - [{ value: toAddress }, { value: tokenAmount }] = params - } + const { confirmTransaction: { tokenProps: { tokenSymbol } = {} } } = state + const { tokenAmount } = approveTokenAmountAndToAddressSelector(state) return { - toAddress, - tokenAddress, tokenAmount, tokenSymbol, } diff --git a/ui/app/components/pages/confirm-send-token/confirm-send-token.component.js b/ui/app/components/pages/confirm-send-token/confirm-send-token.component.js index 46ad9ccab..cb39e3d7b 100644 --- a/ui/app/components/pages/confirm-send-token/confirm-send-token.component.js +++ b/ui/app/components/pages/confirm-send-token/confirm-send-token.component.js @@ -1,20 +1,13 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import ConfirmTransactionBase from '../confirm-transaction-base' +import ConfirmTokenTransactionBase from '../confirm-token-transaction-base' import { SEND_ROUTE } from '../../../routes' export default class ConfirmSendToken extends Component { - static contextTypes = { - t: PropTypes.func, - } - static propTypes = { history: PropTypes.object, - tokenAddress: PropTypes.string, - toAddress: PropTypes.string, - numberOfTokens: PropTypes.number, - tokenSymbol: PropTypes.string, editTransaction: PropTypes.func, + tokenAmount: PropTypes.number, } handleEdit (confirmTransactionData) { @@ -24,15 +17,12 @@ export default class ConfirmSendToken extends Component { } render () { - const { toAddress, tokenAddress, tokenSymbol, numberOfTokens } = this.props + const { tokenAmount } = this.props return ( - <ConfirmTransactionBase - toAddress={toAddress} - identiconAddress={tokenAddress} - title={`${numberOfTokens} ${tokenSymbol}`} + <ConfirmTokenTransactionBase onEdit={confirmTransactionData => this.handleEdit(confirmTransactionData)} - hideSubtitle + tokenAmount={tokenAmount} /> ) } diff --git a/ui/app/components/pages/confirm-send-token/confirm-send-token.container.js b/ui/app/components/pages/confirm-send-token/confirm-send-token.container.js index 2d7efeed6..d60911e59 100644 --- a/ui/app/components/pages/confirm-send-token/confirm-send-token.container.js +++ b/ui/app/components/pages/confirm-send-token/confirm-send-token.container.js @@ -2,36 +2,16 @@ import { connect } from 'react-redux' import { compose } from 'recompose' import { withRouter } from 'react-router-dom' import ConfirmSendToken from './confirm-send-token.component' -import { calcTokenAmount } from '../../../token-util' import { clearConfirmTransaction } from '../../../ducks/confirm-transaction.duck' import { setSelectedToken, updateSend, showSendTokenPage } from '../../../actions' import { conversionUtil } from '../../../conversion-util' +import { sendTokenTokenAmountAndToAddressSelector } from '../../../selectors/confirm-transaction' const mapStateToProps = state => { - const { confirmTransaction } = state - const { - tokenData = {}, - tokenProps: { tokenSymbol, tokenDecimals } = {}, - txData: { txParams: { to: tokenAddress } = {} } = {}, - } = confirmTransaction - const { params = [] } = tokenData - - let toAddress = '' - let tokenAmount = '' - - if (params && params.length === 2) { - [{ value: toAddress }, { value: tokenAmount }] = params - } - - const numberOfTokens = tokenAmount && tokenDecimals - ? calcTokenAmount(tokenAmount, tokenDecimals) - : 0 + const { tokenAmount } = sendTokenTokenAmountAndToAddressSelector(state) return { - toAddress, - tokenAddress, - tokenSymbol, - numberOfTokens, + tokenAmount, } } diff --git a/ui/app/components/pages/confirm-send-token/index.scss b/ui/app/components/pages/confirm-send-token/index.scss deleted file mode 100644 index 0476749f6..000000000 --- a/ui/app/components/pages/confirm-send-token/index.scss +++ /dev/null @@ -1,19 +0,0 @@ -.confirm-send-token { - &__title { - padding: 4px 0; - display: flex; - align-items: center; - } - - &__identicon { - flex: 0 0 auto; - } - - &__title-text { - font-size: 2.25rem; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - padding-left: 8px; - } -} diff --git a/ui/app/components/pages/confirm-token-transaction-base/confirm-token-transaction-base.component.js b/ui/app/components/pages/confirm-token-transaction-base/confirm-token-transaction-base.component.js new file mode 100644 index 000000000..365ae216e --- /dev/null +++ b/ui/app/components/pages/confirm-token-transaction-base/confirm-token-transaction-base.component.js @@ -0,0 +1,85 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import ConfirmTransactionBase from '../confirm-transaction-base' +import { + formatCurrency, + convertTokenToFiat, + addFiat, +} from '../../../helpers/confirm-transaction/util' + +export default class ConfirmTokenTransactionBase extends Component { + static contextTypes = { + t: PropTypes.func, + } + + static propTypes = { + tokenAddress: PropTypes.string, + toAddress: PropTypes.string, + tokenAmount: PropTypes.number, + tokenSymbol: PropTypes.string, + fiatTransactionTotal: PropTypes.string, + ethTransactionTotal: PropTypes.string, + contractExchangeRate: PropTypes.number, + conversionRate: PropTypes.number, + currentCurrency: PropTypes.string, + } + + getFiatTransactionAmount () { + const { tokenAmount, currentCurrency, conversionRate, contractExchangeRate } = this.props + + return convertTokenToFiat({ + value: tokenAmount, + toCurrency: currentCurrency, + conversionRate, + contractExchangeRate, + }) + } + + getSubtitle () { + const { currentCurrency, contractExchangeRate } = this.props + + if (typeof contractExchangeRate === 'undefined') { + return this.context.t('noConversionRateAvailable') + } else { + const fiatTransactionAmount = this.getFiatTransactionAmount() + return formatCurrency(fiatTransactionAmount, currentCurrency) + } + } + + getFiatTotalTextOverride () { + const { fiatTransactionTotal, currentCurrency, contractExchangeRate } = this.props + + if (typeof contractExchangeRate === 'undefined') { + return formatCurrency(fiatTransactionTotal, currentCurrency) + } else { + const fiatTransactionAmount = this.getFiatTransactionAmount() + const fiatTotal = addFiat(fiatTransactionAmount, fiatTransactionTotal) + return formatCurrency(fiatTotal, currentCurrency) + } + } + + render () { + const { + toAddress, + tokenAddress, + tokenSymbol, + tokenAmount, + ethTransactionTotal, + ...restProps + } = this.props + + const tokensText = `${tokenAmount} ${tokenSymbol}` + + return ( + <ConfirmTransactionBase + toAddress={toAddress} + identiconAddress={tokenAddress} + title={tokensText} + subtitle={this.getSubtitle()} + ethTotalTextOverride={`${tokensText} + \u2666 ${ethTransactionTotal}`} + fiatTotalTextOverride={this.getFiatTotalTextOverride()} + {...restProps} + /> + ) + } +} diff --git a/ui/app/components/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js b/ui/app/components/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js new file mode 100644 index 000000000..be38acdb0 --- /dev/null +++ b/ui/app/components/pages/confirm-token-transaction-base/confirm-token-transaction-base.container.js @@ -0,0 +1,34 @@ +import { connect } from 'react-redux' +import ConfirmTokenTransactionBase from './confirm-token-transaction-base.component' +import { + tokenAmountAndToAddressSelector, + contractExchangeRateSelector, +} from '../../../selectors/confirm-transaction' + +const mapStateToProps = (state, ownProps) => { + const { tokenAmount: ownTokenAmount } = ownProps + const { confirmTransaction, metamask: { currentCurrency, conversionRate } } = state + const { + txData: { txParams: { to: tokenAddress } = {} } = {}, + tokenProps: { tokenSymbol } = {}, + fiatTransactionTotal, + ethTransactionTotal, + } = confirmTransaction + + const { tokenAmount, toAddress } = tokenAmountAndToAddressSelector(state) + const contractExchangeRate = contractExchangeRateSelector(state) + + return { + toAddress, + tokenAddress, + tokenAmount: typeof ownTokenAmount !== 'undefined' ? ownTokenAmount : tokenAmount, + tokenSymbol, + currentCurrency, + conversionRate, + contractExchangeRate, + fiatTransactionTotal, + ethTransactionTotal, + } +} + +export default connect(mapStateToProps)(ConfirmTokenTransactionBase) diff --git a/ui/app/components/pages/confirm-token-transaction-base/index.js b/ui/app/components/pages/confirm-token-transaction-base/index.js new file mode 100644 index 000000000..e15c5d56b --- /dev/null +++ b/ui/app/components/pages/confirm-token-transaction-base/index.js @@ -0,0 +1,2 @@ +export { default } from './confirm-token-transaction-base.container' +export { default as ConfirmTokenTransactionBase } from './confirm-token-transaction-base.component' diff --git a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js index 842b34d2e..e1bf2210f 100644 --- a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -2,7 +2,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import ConfirmPageContainer, { ConfirmDetailRow } from '../../confirm-page-container' import { formatCurrency } from '../../../helpers/confirm-transaction/util' -import { isBalanceSufficient } from '../../send_/send.utils' +import { isBalanceSufficient } from '../../send/send.utils' import { DEFAULT_ROUTE } from '../../../routes' import { INSUFFICIENT_FUNDS_ERROR_KEY, @@ -54,6 +54,8 @@ export default class ConfirmTransactionBase extends Component { detailsComponent: PropTypes.node, errorKey: PropTypes.string, errorMessage: PropTypes.string, + ethTotalTextOverride: PropTypes.string, + fiatTotalTextOverride: PropTypes.string, hideData: PropTypes.bool, hideDetails: PropTypes.bool, hideSubtitle: PropTypes.bool, @@ -146,6 +148,8 @@ export default class ConfirmTransactionBase extends Component { currentCurrency, fiatTransactionTotal, ethTransactionTotal, + fiatTotalTextOverride, + ethTotalTextOverride, hideDetails, } = this.props @@ -153,14 +157,16 @@ export default class ConfirmTransactionBase extends Component { return null } + const formattedCurrency = formatCurrency(fiatTransactionTotal, currentCurrency) + return ( detailsComponent || ( <div className="confirm-page-container-content__details"> <div className="confirm-page-container-content__gas-fee"> <ConfirmDetailRow label="Gas Fee" - fiatFee={formatCurrency(fiatTransactionFee, currentCurrency)} - ethFee={ethTransactionFee} + fiatText={formatCurrency(fiatTransactionFee, currentCurrency)} + ethText={`\u2666 ${ethTransactionFee}`} headerText="Edit" headerTextClassName="confirm-detail-row__header-text--edit" onHeaderClick={() => this.handleEditGas()} @@ -169,11 +175,11 @@ export default class ConfirmTransactionBase extends Component { <div> <ConfirmDetailRow label="Total" - fiatFee={formatCurrency(fiatTransactionTotal, currentCurrency)} - ethFee={ethTransactionTotal} + fiatText={fiatTotalTextOverride || formattedCurrency} + ethText={ethTotalTextOverride || `\u2666 ${ethTransactionTotal}`} headerText="Amount + Gas Fee" headerTextClassName="confirm-detail-row__header-text--total" - fiatFeeColor="#2f9ae0" + fiatTextColor="#2f9ae0" /> </div> </div> @@ -206,17 +212,21 @@ export default class ConfirmTransactionBase extends Component { <div className="confirm-page-container-content__data-box-label"> {`${t('functionType')}:`} <span className="confirm-page-container-content__function-type"> - { name } + { name || t('notFound') } </span> </div> - <div className="confirm-page-container-content__data-box"> - <div className="confirm-page-container-content__data-field-label"> - { `${t('parameters')}:` } - </div> - <div> - <pre>{ JSON.stringify(params, null, 2) }</pre> - </div> - </div> + { + params && ( + <div className="confirm-page-container-content__data-box"> + <div className="confirm-page-container-content__data-field-label"> + { `${t('parameters')}:` } + </div> + <div> + <pre>{ JSON.stringify(params, null, 2) }</pre> + </div> + </div> + ) + } <div className="confirm-page-container-content__data-box-label"> {`${t('hexData')}:`} </div> @@ -297,7 +307,7 @@ export default class ConfirmTransactionBase extends Component { toName={toName} toAddress={toAddress} showEdit={onEdit && !isTxReprice} - action={action || name} + action={action || name || this.context.t('unknownFunction')} title={title || `${fiatConvertedAmount} ${currentCurrency.toUpperCase()}`} subtitle={subtitle || `\u2666 ${ethTransactionAmount}`} hideSubtitle={hideSubtitle} diff --git a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js index 31108bbd0..0c0deff18 100644 --- a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -2,6 +2,7 @@ import { connect } from 'react-redux' import { compose } from 'recompose' import { withRouter } from 'react-router-dom' import R from 'ramda' +import contractMap from 'eth-contract-metadata' import ConfirmTransactionBase from './confirm-transaction-base.component' import { clearConfirmTransaction, @@ -13,9 +14,17 @@ import { GAS_LIMIT_TOO_LOW_ERROR_KEY, } from '../../../constants/error-keys' import { getHexGasTotal } from '../../../helpers/confirm-transaction/util' -import { isBalanceSufficient } from '../../send_/send.utils' +import { isBalanceSufficient } from '../../send/send.utils' import { conversionGreaterThan } from '../../../conversion-util' -import { MIN_GAS_LIMIT_DEC } from '../../send_/send.constants' +import { MIN_GAS_LIMIT_DEC } from '../../send/send.constants' +import { addressSlicer } from '../../../util' + +const casedContractMap = Object.keys(contractMap).reduce((acc, base) => { + return { + ...acc, + [base.toLowerCase()]: contractMap[base], + } +}, {}) const mapStateToProps = (state, props) => { const { toAddress: propsToAddress } = props @@ -48,7 +57,10 @@ const mapStateToProps = (state, props) => { const { balance } = accounts[selectedAddress] const { name: fromName } = identities[selectedAddress] const toAddress = propsToAddress || txParamsToAddress - const toName = identities[toAddress] && identities[toAddress].name + const toName = identities[toAddress] + ? identities[toAddress].name + : casedContractMap[toAddress] ? casedContractMap[toAddress].name : addressSlicer(toAddress) + const isTxReprice = Boolean(lastGasPrice) const transaction = R.find(({ id }) => id === transactionId)(selectedAddressTxList) diff --git a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js b/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js index 25259b98c..0280f73c6 100644 --- a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js +++ b/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.component.js @@ -8,11 +8,16 @@ import { CONFIRM_SEND_ETHER_PATH, CONFIRM_SEND_TOKEN_PATH, CONFIRM_APPROVE_PATH, + CONFIRM_TRANSFER_FROM_PATH, CONFIRM_TOKEN_METHOD_PATH, SIGNATURE_REQUEST_PATH, } from '../../../routes' import { isConfirmDeployContract } from './confirm-transaction-switch.util' -import { TOKEN_METHOD_TRANSFER, TOKEN_METHOD_APPROVE } from './confirm-transaction-switch.constants' +import { + TOKEN_METHOD_TRANSFER, + TOKEN_METHOD_APPROVE, + TOKEN_METHOD_TRANSFER_FROM, +} from './confirm-transaction-switch.constants' export default class ConfirmTransactionSwitch extends Component { static propTypes = { @@ -27,8 +32,7 @@ export default class ConfirmTransactionSwitch extends Component { methodData: { name }, fetchingMethodData, } = this.props - const { id } = txData - + const { id, txParams: { data } = {} } = txData if (isConfirmDeployContract(txData)) { const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_DEPLOY_CONTRACT_PATH}` @@ -39,10 +43,10 @@ export default class ConfirmTransactionSwitch extends Component { return <Loading /> } - if (name) { - const methodName = name.toLowerCase() + if (data) { + const methodName = name && name.toLowerCase() - switch (methodName.toLowerCase()) { + switch (methodName) { case TOKEN_METHOD_TRANSFER: { const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_SEND_TOKEN_PATH}` return <Redirect to={{ pathname }} /> @@ -51,6 +55,10 @@ export default class ConfirmTransactionSwitch extends Component { const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_APPROVE_PATH}` return <Redirect to={{ pathname }} /> } + case TOKEN_METHOD_TRANSFER_FROM: { + const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_TRANSFER_FROM_PATH}` + return <Redirect to={{ pathname }} /> + } default: { const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_TOKEN_METHOD_PATH}` return <Redirect to={{ pathname }} /> diff --git a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.constants.js b/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.constants.js index 622d2a37a..9db4a2f96 100644 --- a/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.constants.js +++ b/ui/app/components/pages/confirm-transaction-switch/confirm-transaction-switch.constants.js @@ -1,2 +1,3 @@ export const TOKEN_METHOD_TRANSFER = 'transfer' export const TOKEN_METHOD_APPROVE = 'approve' +export const TOKEN_METHOD_TRANSFER_FROM = 'transferfrom' diff --git a/ui/app/components/pages/confirm-transaction/confirm-transaction.component.js b/ui/app/components/pages/confirm-transaction/confirm-transaction.component.js index 874a89fd2..3ac656d73 100644 --- a/ui/app/components/pages/confirm-transaction/confirm-transaction.component.js +++ b/ui/app/components/pages/confirm-transaction/confirm-transaction.component.js @@ -8,6 +8,7 @@ import ConfirmSendEther from '../confirm-send-ether' import ConfirmSendToken from '../confirm-send-token' import ConfirmDeployContract from '../confirm-deploy-contract' import ConfirmApprove from '../confirm-approve' +import ConfirmTokenTransactionBase from '../confirm-token-transaction-base' import ConfTx from '../../../conf-tx' import { DEFAULT_ROUTE, @@ -16,6 +17,7 @@ import { CONFIRM_SEND_ETHER_PATH, CONFIRM_SEND_TOKEN_PATH, CONFIRM_APPROVE_PATH, + CONFIRM_TRANSFER_FROM_PATH, CONFIRM_TOKEN_METHOD_PATH, SIGNATURE_REQUEST_PATH, } from '../../../routes' @@ -139,6 +141,11 @@ export default class ConfirmTransaction extends Component { /> <Route exact + path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_TRANSFER_FROM_PATH}`} + component={ConfirmTokenTransactionBase} + /> + <Route + exact path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${SIGNATURE_REQUEST_PATH}`} component={ConfTx} /> diff --git a/ui/app/components/pages/create-account/connect-hardware/account-list.js b/ui/app/components/pages/create-account/connect-hardware/account-list.js new file mode 100644 index 000000000..c722d1f55 --- /dev/null +++ b/ui/app/components/pages/create-account/connect-hardware/account-list.js @@ -0,0 +1,143 @@ +const { Component } = require('react') +const PropTypes = require('prop-types') +const h = require('react-hyperscript') +const genAccountLink = require('../../../../../lib/account-link.js') + +class AccountList extends Component { + constructor (props, context) { + super(props) + } + + renderHeader () { + return ( + h('div.hw-connect', [ + h('h3.hw-connect__title', {}, this.context.t('selectAnAccount')), + h('p.hw-connect__msg', {}, this.context.t('selectAnAccountHelp')), + ]) + ) + } + + renderAccounts () { + return h('div.hw-account-list', [ + this.props.accounts.map((a, i) => { + + return h('div.hw-account-list__item', { key: a.address }, [ + h('div.hw-account-list__item__radio', [ + h('input', { + type: 'radio', + name: 'selectedAccount', + id: `address-${i}`, + value: a.index, + onChange: (e) => this.props.onAccountChange(e.target.value), + checked: this.props.selectedAccount === a.index.toString(), + }), + h( + 'label.hw-account-list__item__label', + { + htmlFor: `address-${i}`, + }, + [ + h('span.hw-account-list__item__index', a.index + 1), + `${a.address.slice(0, 4)}...${a.address.slice(-4)}`, + h('span.hw-account-list__item__balance', `${a.balance}`), + ]), + ]), + h( + 'a.hw-account-list__item__link', + { + href: genAccountLink(a.address, this.props.network), + target: '_blank', + title: this.context.t('etherscanView'), + }, + h('img', { src: 'images/popout.svg' }) + ), + ]) + }), + ]) + } + + renderPagination () { + return h('div.hw-list-pagination', [ + h( + 'button.hw-list-pagination__button', + { + onClick: () => this.props.getPage(-1), + }, + `< ${this.context.t('prev')}` + ), + + h( + 'button.hw-list-pagination__button', + { + onClick: () => this.props.getPage(1), + }, + `${this.context.t('next')} >` + ), + ]) + } + + renderButtons () { + const disabled = this.props.selectedAccount === null + const buttonProps = {} + if (disabled) { + buttonProps.disabled = true + } + + return h('div.new-account-connect-form__buttons', {}, [ + h( + 'button.btn-default.btn--large.new-account-connect-form__button', + { + onClick: this.props.onCancel.bind(this), + }, + [this.context.t('cancel')] + ), + + h( + `button.btn-primary.btn--large.new-account-connect-form__button.unlock ${disabled ? '.btn-primary--disabled' : ''}`, + { + onClick: this.props.onUnlockAccount.bind(this), + ...buttonProps, + }, + [this.context.t('unlock')] + ), + ]) + } + + renderForgetDevice () { + return h('div.hw-forget-device-container', {}, [ + h('a', { + onClick: this.props.onForgetDevice.bind(this), + }, this.context.t('forgetDevice')), + ]) + } + + render () { + return h('div.new-account-connect-form.account-list', {}, [ + this.renderHeader(), + this.renderAccounts(), + this.renderPagination(), + this.renderButtons(), + this.renderForgetDevice(), + ]) + } + +} + + +AccountList.propTypes = { + accounts: PropTypes.array.isRequired, + onAccountChange: PropTypes.func.isRequired, + onForgetDevice: PropTypes.func.isRequired, + getPage: PropTypes.func.isRequired, + network: PropTypes.string, + selectedAccount: PropTypes.string, + history: PropTypes.object, + onUnlockAccount: PropTypes.func, + onCancel: PropTypes.func, +} + +AccountList.contextTypes = { + t: PropTypes.func, +} + +module.exports = AccountList diff --git a/ui/app/components/pages/create-account/connect-hardware/connect-screen.js b/ui/app/components/pages/create-account/connect-hardware/connect-screen.js new file mode 100644 index 000000000..cb2b86595 --- /dev/null +++ b/ui/app/components/pages/create-account/connect-hardware/connect-screen.js @@ -0,0 +1,149 @@ +const { Component } = require('react') +const PropTypes = require('prop-types') +const h = require('react-hyperscript') + +class ConnectScreen extends Component { + constructor (props, context) { + super(props) + } + + renderUnsupportedBrowser () { + return ( + h('div.new-account-connect-form.unsupported-browser', {}, [ + h('div.hw-connect', [ + h('h3.hw-connect__title', {}, this.context.t('browserNotSupported')), + h('p.hw-connect__msg', {}, this.context.t('chromeRequiredForTrezor')), + ]), + h( + 'button.btn-primary.btn--large', + { + onClick: () => global.platform.openWindow({ + url: 'https://google.com/chrome', + }), + }, + this.context.t('downloadGoogleChrome') + ), + ]) + ) + } + + renderHeader () { + return ( + h('div.hw-connect__header', {}, [ + h('h3.hw-connect__header__title', {}, this.context.t(`hardwareSupport`)), + h('p.hw-connect__header__msg', {}, this.context.t(`hardwareSupportMsg`)), + ]) + ) + } + + renderTrezorAffiliateLink () { + return h('div.hw-connect__get-trezor', {}, [ + h('p.hw-connect__get-trezor__msg', {}, this.context.t(`dontHaveATrezorWallet`)), + h('a.hw-connect__get-trezor__link', { + href: 'https://shop.trezor.io/?a=metamask', + target: '_blank', + }, this.context.t('orderOneHere')), + ]) + } + + renderConnectToTrezorButton () { + return h( + 'button.btn-primary.btn--large', + { onClick: this.props.connectToTrezor.bind(this) }, + this.props.btnText + ) + } + + scrollToTutorial = (e) => { + if (this.referenceNode) this.referenceNode.scrollIntoView({behavior: 'smooth'}) + } + + renderLearnMore () { + return ( + h('p.hw-connect__learn-more', { + onClick: this.scrollToTutorial, + }, [ + this.context.t('learnMore'), + h('img.hw-connect__learn-more__arrow', { src: 'images/caret-right.svg'}), + ]) + ) + } + + renderTutorialSteps () { + const steps = [ + { + asset: 'hardware-wallet-step-1', + dimensions: {width: '225px', height: '75px'}, + }, + { + asset: 'hardware-wallet-step-2', + dimensions: {width: '300px', height: '100px'}, + }, + { + asset: 'hardware-wallet-step-3', + dimensions: {width: '120px', height: '90px'}, + }, + ] + + return h('.hw-tutorial', { + ref: node => { this.referenceNode = node }, + }, + steps.map((step, i) => ( + h('div.hw-connect', {}, [ + h('h3.hw-connect__title', {}, this.context.t(`step${i + 1}HardwareWallet`)), + h('p.hw-connect__msg', {}, this.context.t(`step${i + 1}HardwareWalletMsg`)), + h('img.hw-connect__step-asset', { src: `images/${step.asset}.svg`, ...step.dimensions }), + ]) + )) + ) + } + + renderFooter () { + return ( + h('div.hw-connect__footer', {}, [ + h('h3.hw-connect__footer__title', {}, this.context.t(`readyToConnect`)), + this.renderConnectToTrezorButton(), + h('p.hw-connect__footer__msg', {}, [ + this.context.t(`havingTroubleConnecting`), + h('a.hw-connect__footer__link', { + href: 'https://support.metamask.io/', + target: '_blank', + }, this.context.t('getHelp')), + ]), + ]) + ) + } + + renderConnectScreen () { + return ( + h('div.new-account-connect-form', {}, [ + this.renderHeader(), + this.renderTrezorAffiliateLink(), + this.renderConnectToTrezorButton(), + this.renderLearnMore(), + this.renderTutorialSteps(), + this.renderFooter(), + ]) + ) + } + + render () { + if (this.props.browserSupported) { + return this.renderConnectScreen() + } + return this.renderUnsupportedBrowser() + } +} + +ConnectScreen.propTypes = { + connectToTrezor: PropTypes.func.isRequired, + btnText: PropTypes.string.isRequired, + browserSupported: PropTypes.bool.isRequired, +} + +ConnectScreen.contextTypes = { + t: PropTypes.func, +} + +module.exports = ConnectScreen + diff --git a/ui/app/components/pages/create-account/connect-hardware/index.js b/ui/app/components/pages/create-account/connect-hardware/index.js new file mode 100644 index 000000000..cc3761c04 --- /dev/null +++ b/ui/app/components/pages/create-account/connect-hardware/index.js @@ -0,0 +1,234 @@ +const { Component } = require('react') +const PropTypes = require('prop-types') +const h = require('react-hyperscript') +const connect = require('react-redux').connect +const actions = require('../../../../actions') +const ConnectScreen = require('./connect-screen') +const AccountList = require('./account-list') +const { DEFAULT_ROUTE } = require('../../../../routes') +const { formatBalance } = require('../../../../util') + +class ConnectHardwareForm extends Component { + constructor (props, context) { + super(props) + this.state = { + error: null, + btnText: context.t('connectToTrezor'), + selectedAccount: null, + accounts: [], + browserSupported: true, + } + } + + componentWillReceiveProps (nextProps) { + const { accounts } = nextProps + const newAccounts = this.state.accounts.map(a => { + const normalizedAddress = a.address.toLowerCase() + const balanceValue = accounts[normalizedAddress] && accounts[normalizedAddress].balance || null + a.balance = balanceValue ? formatBalance(balanceValue, 6) : '...' + return a + }) + this.setState({accounts: newAccounts}) + } + + + async componentDidMount () { + const unlocked = await this.props.checkHardwareStatus('trezor') + if (unlocked) { + this.getPage(0) + } + } + + connectToTrezor = () => { + if (this.state.accounts.length) { + return null + } + this.setState({ btnText: this.context.t('connecting')}) + this.getPage(0) + } + + onAccountChange = (account) => { + this.setState({selectedAccount: account.toString(), error: null}) + } + + showTemporaryAlert () { + this.props.showAlert(this.context.t('hardwareWalletConnected')) + // Autohide the alert after 5 seconds + setTimeout(_ => { + this.props.hideAlert() + }, 5000) + } + + getPage = (page) => { + this.props + .connectHardware('trezor', page) + .then(accounts => { + if (accounts.length) { + + // If we just loaded the accounts for the first time + // show the global alert + if (this.state.accounts.length === 0) { + this.showTemporaryAlert() + } + + const newState = {} + // Default to the first account + if (this.state.selectedAccount === null) { + accounts.forEach((a, i) => { + if (a.address.toLowerCase() === this.props.address) { + newState.selectedAccount = a.index.toString() + } + }) + // If the page doesn't contain the selected account, let's deselect it + } else if (!accounts.filter(a => a.index.toString() === this.state.selectedAccount).length) { + newState.selectedAccount = null + } + + + // Map accounts with balances + newState.accounts = accounts.map(account => { + const normalizedAddress = account.address.toLowerCase() + const balanceValue = this.props.accounts[normalizedAddress] && this.props.accounts[normalizedAddress].balance || null + account.balance = balanceValue ? formatBalance(balanceValue, 6) : '...' + return account + }) + + this.setState(newState) + } + }) + .catch(e => { + if (e === 'Window blocked') { + this.setState({ browserSupported: false }) + } + this.setState({ btnText: this.context.t('connectToTrezor') }) + }) + } + + onForgetDevice = () => { + this.props.forgetDevice('trezor') + .then(_ => { + this.setState({ + error: null, + btnText: this.context.t('connectToTrezor'), + selectedAccount: null, + accounts: [], + }) + }).catch(e => { + this.setState({ error: e.toString() }) + }) + } + + onUnlockAccount = () => { + + if (this.state.selectedAccount === null) { + this.setState({ error: this.context.t('accountSelectionRequired') }) + } + + this.props.unlockTrezorAccount(this.state.selectedAccount) + .then(_ => { + this.props.history.push(DEFAULT_ROUTE) + }).catch(e => { + this.setState({ error: e.toString() }) + }) + } + + onCancel = () => { + this.props.history.push(DEFAULT_ROUTE) + } + + renderError () { + return this.state.error + ? h('span.error', { style: { marginBottom: 40 } }, this.state.error) + : null + } + + renderContent () { + if (!this.state.accounts.length) { + return h(ConnectScreen, { + connectToTrezor: this.connectToTrezor, + btnText: this.state.btnText, + browserSupported: this.state.browserSupported, + }) + } + + return h(AccountList, { + accounts: this.state.accounts, + selectedAccount: this.state.selectedAccount, + onAccountChange: this.onAccountChange, + network: this.props.network, + getPage: this.getPage, + history: this.props.history, + onUnlockAccount: this.onUnlockAccount, + onForgetDevice: this.onForgetDevice, + onCancel: this.onCancel, + }) + } + + render () { + return h('div', [ + this.renderError(), + this.renderContent(), + ]) + } +} + +ConnectHardwareForm.propTypes = { + hideModal: PropTypes.func, + showImportPage: PropTypes.func, + showConnectPage: PropTypes.func, + connectHardware: PropTypes.func, + checkHardwareStatus: PropTypes.func, + forgetDevice: PropTypes.func, + showAlert: PropTypes.func, + hideAlert: PropTypes.func, + unlockTrezorAccount: PropTypes.func, + numberOfExistingAccounts: PropTypes.number, + history: PropTypes.object, + t: PropTypes.func, + network: PropTypes.string, + accounts: PropTypes.object, + address: PropTypes.string, +} + +const mapStateToProps = state => { + const { + metamask: { network, selectedAddress, identities = {}, accounts = [] }, + } = state + const numberOfExistingAccounts = Object.keys(identities).length + + return { + network, + accounts, + address: selectedAddress, + numberOfExistingAccounts, + } +} + +const mapDispatchToProps = dispatch => { + return { + connectHardware: (deviceName, page) => { + return dispatch(actions.connectHardware(deviceName, page)) + }, + checkHardwareStatus: (deviceName) => { + return dispatch(actions.checkHardwareStatus(deviceName)) + }, + forgetDevice: (deviceName) => { + return dispatch(actions.forgetDevice(deviceName)) + }, + unlockTrezorAccount: index => { + return dispatch(actions.unlockTrezorAccount(index)) + }, + showImportPage: () => dispatch(actions.showImportPage()), + showConnectPage: () => dispatch(actions.showConnectPage()), + showAlert: (msg) => dispatch(actions.showAlert(msg)), + hideAlert: () => dispatch(actions.hideAlert()), + } +} + +ConnectHardwareForm.contextTypes = { + t: PropTypes.func, +} + +module.exports = connect(mapStateToProps, mapDispatchToProps)( + ConnectHardwareForm +) diff --git a/ui/app/components/pages/create-account/index.js b/ui/app/components/pages/create-account/index.js index 5681e43a9..d3de1ea01 100644 --- a/ui/app/components/pages/create-account/index.js +++ b/ui/app/components/pages/create-account/index.js @@ -8,7 +8,12 @@ const { getCurrentViewContext } = require('../../../selectors') const classnames = require('classnames') const NewAccountCreateForm = require('./new-account') const NewAccountImportForm = require('./import-account') -const { NEW_ACCOUNT_ROUTE, IMPORT_ACCOUNT_ROUTE } = require('../../../routes') +const ConnectHardwareForm = require('./connect-hardware') +const { + NEW_ACCOUNT_ROUTE, + IMPORT_ACCOUNT_ROUTE, + CONNECT_HARDWARE_ROUTE, +} = require('../../../routes') class CreateAccountPage extends Component { renderTabs () { @@ -36,6 +41,19 @@ class CreateAccountPage extends Component { }, [ this.context.t('import'), ]), + h( + 'div.new-account__tabs__tab', + { + className: classnames('new-account__tabs__tab', { + 'new-account__tabs__selected': matchPath(location.pathname, { + path: CONNECT_HARDWARE_ROUTE, + exact: true, + }), + }), + onClick: () => history.push(CONNECT_HARDWARE_ROUTE), + }, + this.context.t('connect') + ), ]) } @@ -57,6 +75,11 @@ class CreateAccountPage extends Component { path: IMPORT_ACCOUNT_ROUTE, component: NewAccountImportForm, }), + h(Route, { + exact: true, + path: CONNECT_HARDWARE_ROUTE, + component: ConnectHardwareForm, + }), ]), ]), ]) diff --git a/ui/app/components/pages/create-account/new-account.js b/ui/app/components/pages/create-account/new-account.js index 9c94990e0..402b8f03b 100644 --- a/ui/app/components/pages/create-account/new-account.js +++ b/ui/app/components/pages/create-account/new-account.js @@ -62,6 +62,7 @@ class NewAccountCreateForm extends Component { NewAccountCreateForm.propTypes = { hideModal: PropTypes.func, showImportPage: PropTypes.func, + showConnectPage: PropTypes.func, createAccount: PropTypes.func, numberOfExistingAccounts: PropTypes.number, history: PropTypes.object, @@ -92,6 +93,7 @@ const mapDispatchToProps = dispatch => { }) }, showImportPage: () => dispatch(actions.showImportPage()), + showConnectPage: () => dispatch(actions.showConnectPage()), } } diff --git a/ui/app/components/pages/index.scss b/ui/app/components/pages/index.scss index 8b333b6a8..b15c59863 100644 --- a/ui/app/components/pages/index.scss +++ b/ui/app/components/pages/index.scss @@ -3,5 +3,3 @@ @import './add-token/index'; @import './confirm-add-token/index'; - -@import './confirm-send-token/index'; diff --git a/ui/app/components/pending-tx/confirm-deploy-contract.js b/ui/app/components/pending-tx/confirm-deploy-contract.js deleted file mode 100644 index af3a14f57..000000000 --- a/ui/app/components/pending-tx/confirm-deploy-contract.js +++ /dev/null @@ -1,358 +0,0 @@ -const { Component } = require('react') -const connect = require('react-redux').connect -const h = require('react-hyperscript') -const PropTypes = require('prop-types') -const actions = require('../../actions') -const clone = require('clone') -const ethUtil = require('ethereumjs-util') -const BN = ethUtil.BN -const hexToBn = require('../../../../app/scripts/lib/hex-to-bn') -const { conversionUtil } = require('../../conversion-util') -const SenderToRecipient = require('../sender-to-recipient') -const NetworkDisplay = require('../network-display') - -const { MIN_GAS_PRICE_HEX } = require('../send_/send.constants') - -class ConfirmDeployContract extends Component { - constructor (props) { - super(props) - - this.state = { - valid: false, - submitting: false, - } - } - - onSubmit (event) { - event.preventDefault() - const txMeta = this.gatherTxMeta() - const valid = this.checkValidity() - this.setState({ valid, submitting: true }) - - if (valid && this.verifyGasParams()) { - this.props.sendTransaction(txMeta, event) - } else { - this.props.displayWarning(this.context.t('invalidGasParams')) - this.setState({ submitting: false }) - } - } - - cancel (event, txMeta) { - event.preventDefault() - this.props.cancelTransaction(txMeta) - } - - checkValidity () { - const form = this.getFormEl() - const valid = form.checkValidity() - return valid - } - - getFormEl () { - const form = document.querySelector('form#pending-tx-form') - // Stub out form for unit tests: - if (!form) { - return { checkValidity () { return true } } - } - return form - } - - // After a customizable state value has been updated, - gatherTxMeta () { - const props = this.props - const state = this.state - const txData = clone(state.txData) || clone(props.txData) - - // log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`) - return txData - } - - verifyGasParams () { - // We call this in case the gas has not been modified at all - if (!this.state) { return true } - return ( - this._notZeroOrEmptyString(this.state.gas) && - this._notZeroOrEmptyString(this.state.gasPrice) - ) - } - - _notZeroOrEmptyString (obj) { - return obj !== '' && obj !== '0x0' - } - - bnMultiplyByFraction (targetBN, numerator, denominator) { - const numBN = new BN(numerator) - const denomBN = new BN(denominator) - return targetBN.mul(numBN).div(denomBN) - } - - getData () { - const { identities } = this.props - const txMeta = this.gatherTxMeta() - const txParams = txMeta.txParams || {} - - return { - from: { - address: txParams.from, - name: identities[txParams.from].name, - }, - memo: txParams.memo || '', - } - } - - getAmount () { - const { conversionRate, currentCurrency } = this.props - const txMeta = this.gatherTxMeta() - const txParams = txMeta.txParams || {} - - const FIAT = conversionUtil(txParams.value, { - fromNumericBase: 'hex', - toNumericBase: 'dec', - fromCurrency: 'ETH', - toCurrency: currentCurrency, - numberOfDecimals: 2, - fromDenomination: 'WEI', - conversionRate, - }) - const ETH = conversionUtil(txParams.value, { - fromNumericBase: 'hex', - toNumericBase: 'dec', - fromCurrency: 'ETH', - toCurrency: 'ETH', - fromDenomination: 'WEI', - conversionRate, - numberOfDecimals: 6, - }) - - return { - fiat: Number(FIAT), - token: Number(ETH), - } - - } - - getGasFee () { - const { conversionRate, currentCurrency } = this.props - const txMeta = this.gatherTxMeta() - const txParams = txMeta.txParams || {} - - // Gas - const gas = txParams.gas - const gasBn = hexToBn(gas) - - // Gas Price - const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_HEX - const gasPriceBn = hexToBn(gasPrice) - - const txFeeBn = gasBn.mul(gasPriceBn) - - const FIAT = conversionUtil(txFeeBn, { - fromNumericBase: 'BN', - toNumericBase: 'dec', - fromDenomination: 'WEI', - fromCurrency: 'ETH', - toCurrency: currentCurrency, - numberOfDecimals: 2, - conversionRate, - }) - const ETH = conversionUtil(txFeeBn, { - fromNumericBase: 'BN', - toNumericBase: 'dec', - fromDenomination: 'WEI', - fromCurrency: 'ETH', - toCurrency: 'ETH', - numberOfDecimals: 6, - conversionRate, - }) - - return { - fiat: Number(FIAT), - eth: Number(ETH), - } - } - - renderGasFee () { - const { currentCurrency } = this.props - const { fiat: fiatGas, eth: ethGas } = this.getGasFee() - - return ( - h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ this.context.t('gasFee') ]), - h('div.confirm-screen-section-column', [ - h('div.confirm-screen-row-info', `${fiatGas} ${currentCurrency.toUpperCase()}`), - - h( - 'div.confirm-screen-row-detail', - `${ethGas} ETH` - ), - ]), - ]) - ) - } - - renderHeroAmount () { - const { currentCurrency } = this.props - const { fiat: fiatAmount } = this.getAmount() - const txMeta = this.gatherTxMeta() - const txParams = txMeta.txParams || {} - const { memo = '' } = txParams - - return ( - h('div.confirm-send-token__hero-amount-wrapper', [ - h('h3.flex-center.confirm-screen-send-amount', `${fiatAmount}`), - h('h3.flex-center.confirm-screen-send-amount-currency', currentCurrency.toUpperCase()), - h('div.flex-center.confirm-memo-wrapper', [ - h('h3.confirm-screen-send-memo', memo), - ]), - ]) - ) - } - - renderTotalPlusGas () { - const { currentCurrency } = this.props - const { fiat: fiatAmount, token: tokenAmount } = this.getAmount() - const { fiat: fiatGas, eth: ethGas } = this.getGasFee() - - return ( - h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [ - h('div.confirm-screen-section-column', [ - h('span.confirm-screen-label', [ this.context.t('total') + ' ' ]), - h('div.confirm-screen-total-box__subtitle', [ this.context.t('amountPlusGas') ]), - ]), - - h('div.confirm-screen-section-column', [ - h('div.confirm-screen-row-info', `${fiatAmount + fiatGas} ${currentCurrency.toUpperCase()}`), - h('div.confirm-screen-row-detail', `${tokenAmount + ethGas} ETH`), - ]), - ]) - ) - } - - render () { - const { backToAccountDetail, selectedAddress } = this.props - const txMeta = this.gatherTxMeta() - - const { - from: { - address: fromAddress, - name: fromName, - }, - } = this.getData() - - this.inputs = [] - - return ( - h('.page-container', [ - h('.page-container__header', [ - h('.page-container__header-row', [ - h('span.page-container__back-button', { - onClick: () => backToAccountDetail(selectedAddress), - }, this.context.t('back')), - window.METAMASK_UI_TYPE === 'notification' && h(NetworkDisplay), - ]), - h('.page-container__title', this.context.t('confirmContract')), - h('.page-container__subtitle', this.context.t('pleaseReviewTransaction')), - ]), - // Main Send token Card - h('.page-container__content', [ - - h(SenderToRecipient, { - senderName: fromName, - senderAddress: fromAddress, - }), - - // h('h3.flex-center.confirm-screen-sending-to-message', { - // style: { - // textAlign: 'center', - // fontSize: '16px', - // }, - // }, [ - // `You're deploying a new contract.`, - // ]), - - this.renderHeroAmount(), - - h('div.confirm-screen-rows', [ - h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ this.context.t('from') ]), - h('div.confirm-screen-section-column', [ - h('div.confirm-screen-row-info', fromName), - h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`), - ]), - ]), - - h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ this.context.t('to') ]), - h('div.confirm-screen-section-column', [ - h('div.confirm-screen-row-info', this.context.t('newContract')), - ]), - ]), - - this.renderGasFee(), - - this.renderTotalPlusGas(), - - ]), - ]), - - h('form#pending-tx-form', { - onSubmit: event => this.onSubmit(event), - }, [ - h('.page-container__footer', [ - // Cancel Button - h('button.btn-cancel.page-container__footer-button.allcaps', { - onClick: event => this.cancel(event, txMeta), - }, this.context.t('cancel')), - - // Accept Button - h('button.btn-confirm.page-container__footer-button.allcaps', { - onClick: event => this.onSubmit(event), - }, this.context.t('confirm')), - ]), - ]), - ]) - ) - } -} - -ConfirmDeployContract.propTypes = { - sendTransaction: PropTypes.func, - cancelTransaction: PropTypes.func, - backToAccountDetail: PropTypes.func, - displayWarning: PropTypes.func, - identities: PropTypes.object, - conversionRate: PropTypes.number, - currentCurrency: PropTypes.string, - selectedAddress: PropTypes.string, - t: PropTypes.func, -} - -const mapStateToProps = state => { - const { - conversionRate, - identities, - currentCurrency, - } = state.metamask - const accounts = state.metamask.accounts - const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0] - return { - currentCurrency, - conversionRate, - identities, - selectedAddress, - } -} - -const mapDispatchToProps = dispatch => { - return { - backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)), - cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })), - displayWarning: warning => actions.displayWarning(warning), - } -} - -ConfirmDeployContract.contextTypes = { - t: PropTypes.func, -} - -module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmDeployContract) diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js deleted file mode 100644 index 22b2670d8..000000000 --- a/ui/app/components/pending-tx/confirm-send-ether.js +++ /dev/null @@ -1,692 +0,0 @@ -const Component = require('react').Component -const { withRouter } = require('react-router-dom') -const { compose } = require('recompose') -const PropTypes = require('prop-types') -const connect = require('react-redux').connect -const h = require('react-hyperscript') -const inherits = require('util').inherits -const actions = require('../../actions') -const clone = require('clone') -const ethUtil = require('ethereumjs-util') -const BN = ethUtil.BN -const hexToBn = require('../../../../app/scripts/lib/hex-to-bn') -const classnames = require('classnames') -const { - conversionUtil, - addCurrencies, - multiplyCurrencies, -} = require('../../conversion-util') -const { - calcGasTotal, - isBalanceSufficient, -} = require('../send_/send.utils') -const GasFeeDisplay = require('../send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component').default -const SenderToRecipient = require('../sender-to-recipient') -const NetworkDisplay = require('../network-display') -const currencyFormatter = require('currency-formatter') -const currencies = require('currency-formatter/currencies') - -const { MIN_GAS_PRICE_HEX } = require('../send_/send.constants') -const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes') -const { - ENVIRONMENT_TYPE_POPUP, - ENVIRONMENT_TYPE_NOTIFICATION, -} = require('../../../../app/scripts/lib/enums') - -import { - updateSendErrors, -} from '../../ducks/send.duck' - -ConfirmSendEther.contextTypes = { - t: PropTypes.func, -} - -module.exports = compose( - withRouter, - connect(mapStateToProps, mapDispatchToProps) -)(ConfirmSendEther) - - -function mapStateToProps (state) { - const { - conversionRate, - identities, - currentCurrency, - send, - } = state.metamask - const accounts = state.metamask.accounts - const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0] - const { balance } = accounts[selectedAddress] - return { - conversionRate, - identities, - selectedAddress, - currentCurrency, - send, - balance, - } -} - -function mapDispatchToProps (dispatch) { - return { - clearSend: () => dispatch(actions.clearSend()), - editTransaction: txMeta => { - const { id, txParams } = txMeta - const { - gas: gasLimit, - gasPrice, - to, - value: amount, - } = txParams - - dispatch(actions.updateSend({ - gasLimit, - gasPrice, - gasTotal: null, - to, - amount, - errors: { to: null, amount: null }, - editingTransactionId: id, - })) - }, - cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })), - showCustomizeGasModal: (txMeta, sendGasLimit, sendGasPrice, sendGasTotal) => { - const { id, txParams, lastGasPrice } = txMeta - const { gas: txGasLimit, gasPrice: txGasPrice } = txParams - - let forceGasMin - if (lastGasPrice) { - forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, { - multiplicandBase: 16, - multiplierBase: 10, - toNumericBase: 'hex', - fromDenomination: 'WEI', - })) - } - - dispatch(actions.updateSend({ - gasLimit: sendGasLimit || txGasLimit, - gasPrice: sendGasPrice || txGasPrice, - editingTransactionId: id, - gasTotal: sendGasTotal, - forceGasMin, - })) - dispatch(actions.showModal({ name: 'CUSTOMIZE_GAS' })) - }, - updateSendErrors: error => dispatch(updateSendErrors(error)), - } -} - -inherits(ConfirmSendEther, Component) -function ConfirmSendEther () { - Component.call(this) - this.state = {} - this.onSubmit = this.onSubmit.bind(this) -} - -ConfirmSendEther.prototype.updateComponentSendErrors = function (prevProps) { - const { - balance: oldBalance, - conversionRate: oldConversionRate, - } = prevProps - const { - updateSendErrors, - balance, - conversionRate, - send: { - errors: { - simulationFails, - }, - }, - } = this.props - const txMeta = this.gatherTxMeta() - - const shouldUpdateBalanceSendErrors = balance && [ - balance !== oldBalance, - conversionRate !== oldConversionRate, - ].some(x => Boolean(x)) - - if (shouldUpdateBalanceSendErrors) { - const balanceIsSufficient = this.isBalanceSufficient(txMeta) - updateSendErrors({ - insufficientFunds: balanceIsSufficient ? false : 'insufficientFunds', - }) - } - - const shouldUpdateSimulationSendError = Boolean(txMeta.simulationFails) !== Boolean(simulationFails) - - if (shouldUpdateSimulationSendError) { - updateSendErrors({ - simulationFails: !txMeta.simulationFails ? false : 'transactionError', - }) - } -} - -ConfirmSendEther.prototype.componentWillMount = function () { - this.updateComponentSendErrors({}) -} - -ConfirmSendEther.prototype.componentDidUpdate = function (prevProps) { - this.updateComponentSendErrors(prevProps) -} - -ConfirmSendEther.prototype.getAmount = function () { - const { conversionRate, currentCurrency } = this.props - const txMeta = this.gatherTxMeta() - const txParams = txMeta.txParams || {} - - const FIAT = conversionUtil(txParams.value, { - fromNumericBase: 'hex', - toNumericBase: 'dec', - fromCurrency: 'ETH', - toCurrency: currentCurrency, - numberOfDecimals: 2, - fromDenomination: 'WEI', - conversionRate, - }) - const ETH = conversionUtil(txParams.value, { - fromNumericBase: 'hex', - toNumericBase: 'dec', - fromCurrency: 'ETH', - toCurrency: 'ETH', - fromDenomination: 'WEI', - conversionRate, - numberOfDecimals: 6, - }) - - return { - FIAT, - ETH, - } - -} - -ConfirmSendEther.prototype.getGasFee = function () { - const { conversionRate, currentCurrency } = this.props - const txMeta = this.gatherTxMeta() - const txParams = txMeta.txParams || {} - - // Gas - const gas = txParams.gas - const gasBn = hexToBn(gas) - - // From latest master -// const gasLimit = new BN(parseInt(blockGasLimit)) -// const safeGasLimitBN = this.bnMultiplyByFraction(gasLimit, 19, 20) -// const saferGasLimitBN = this.bnMultiplyByFraction(gasLimit, 18, 20) -// const safeGasLimit = safeGasLimitBN.toString(10) - - // Gas Price - const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_HEX - const gasPriceBn = hexToBn(gasPrice) - - const txFeeBn = gasBn.mul(gasPriceBn) - - const FIAT = conversionUtil(txFeeBn, { - fromNumericBase: 'BN', - toNumericBase: 'dec', - fromDenomination: 'WEI', - fromCurrency: 'ETH', - toCurrency: currentCurrency, - numberOfDecimals: 2, - conversionRate, - }) - const ETH = conversionUtil(txFeeBn, { - fromNumericBase: 'BN', - toNumericBase: 'dec', - fromDenomination: 'WEI', - fromCurrency: 'ETH', - toCurrency: 'ETH', - numberOfDecimals: 6, - conversionRate, - }) - - return { - FIAT, - ETH, - gasFeeInHex: txFeeBn.toString(16), - } -} - -ConfirmSendEther.prototype.getData = function () { - const { identities } = this.props - const txMeta = this.gatherTxMeta() - const txParams = txMeta.txParams || {} - const account = identities ? identities[txParams.from] || {} : {} - const { FIAT: gasFeeInFIAT, ETH: gasFeeInETH, gasFeeInHex } = this.getGasFee() - const { FIAT: amountInFIAT, ETH: amountInETH } = this.getAmount() - - const totalInFIAT = addCurrencies(gasFeeInFIAT, amountInFIAT, { - toNumericBase: 'dec', - numberOfDecimals: 2, - }) - const totalInETH = addCurrencies(gasFeeInETH, amountInETH, { - toNumericBase: 'dec', - numberOfDecimals: 6, - }) - - return { - from: { - address: txParams.from, - name: account.name, - }, - to: { - address: txParams.to, - name: identities[txParams.to] ? identities[txParams.to].name : this.context.t('newRecipient'), - }, - memo: txParams.memo || '', - gasFeeInFIAT, - gasFeeInETH, - amountInFIAT, - amountInETH, - totalInFIAT, - totalInETH, - gasFeeInHex, - } -} - -ConfirmSendEther.prototype.convertToRenderableCurrency = function (value, currencyCode) { - const upperCaseCurrencyCode = currencyCode.toUpperCase() - - return currencies.find(currency => currency.code === upperCaseCurrencyCode) - ? currencyFormatter.format(Number(value), { - code: upperCaseCurrencyCode, - }) - : value -} - -ConfirmSendEther.prototype.editTransaction = function () { - const { editTransaction, history } = this.props - const txMeta = this.gatherTxMeta() - editTransaction(txMeta) - history.push(SEND_ROUTE) -} - -ConfirmSendEther.prototype.renderHeaderRow = function (isTxReprice) { - const windowType = window.METAMASK_UI_TYPE - const isFullScreen = windowType !== ENVIRONMENT_TYPE_NOTIFICATION && - windowType !== ENVIRONMENT_TYPE_POPUP - - if (isTxReprice && isFullScreen) { - return null - } - - return ( - h('.page-container__header-row', [ - h('span.page-container__back-button', { - onClick: () => this.editTransaction(), - style: { - visibility: isTxReprice ? 'hidden' : 'initial', - }, - }, 'Edit'), - !isFullScreen && h(NetworkDisplay), - ]) - ) -} - -ConfirmSendEther.prototype.renderHeader = function (isTxReprice) { - const title = isTxReprice ? this.context.t('speedUpTitle') : this.context.t('confirm') - const subtitle = isTxReprice - ? this.context.t('speedUpSubtitle') - : this.context.t('pleaseReviewTransaction') - - return ( - h('.page-container__header', [ - this.renderHeaderRow(isTxReprice), - h('.page-container__title', title), - h('.page-container__subtitle', subtitle), - ]) - ) -} - -ConfirmSendEther.prototype.render = function () { - const { - currentCurrency, - clearSend, - conversionRate, - currentCurrency: convertedCurrency, - showCustomizeGasModal, - send: { - gasTotal, - gasLimit: sendGasLimit, - gasPrice: sendGasPrice, - errors, - }, - } = this.props - const txMeta = this.gatherTxMeta() - const isTxReprice = Boolean(txMeta.lastGasPrice) - const txParams = txMeta.txParams || {} - - const { - from: { - address: fromAddress, - name: fromName, - }, - to: { - address: toAddress, - name: toName, - }, - memo, - gasFeeInHex, - amountInFIAT, - totalInFIAT, - totalInETH, - } = this.getData() - - const convertedAmountInFiat = this.convertToRenderableCurrency(amountInFIAT, currentCurrency) - const convertedTotalInFiat = this.convertToRenderableCurrency(totalInFIAT, currentCurrency) - - // This is from the latest master - // It handles some of the errors that we are not currently handling - // Leaving as comments fo reference - - // const balanceBn = hexToBn(balance) - // const insufficientBalance = balanceBn.lt(maxCost) - // const buyDisabled = insufficientBalance || !this.state.valid || !isValidAddress || this.state.submitting - // const showRejectAll = props.unconfTxListLength > 1 -// const dangerousGasLimit = gasBn.gte(saferGasLimitBN) -// const gasLimitSpecified = txMeta.gasLimitSpecified - - this.inputs = [] - - return ( - // Main Send token Card - h('.page-container', [ - this.renderHeader(isTxReprice), - h('.page-container__content', [ - h(SenderToRecipient, { - senderName: fromName, - senderAddress: fromAddress, - recipientName: toName, - recipientAddress: txParams.to, - }), - - // h('h3.flex-center.confirm-screen-sending-to-message', { - // style: { - // textAlign: 'center', - // fontSize: '16px', - // }, - // }, [ - // `You're sending to Recipient ...${toAddress.slice(toAddress.length - 4)}`, - // ]), - - h('h3.flex-center.confirm-screen-send-amount', [`${convertedAmountInFiat}`]), - h('h3.flex-center.confirm-screen-send-amount-currency', [ currentCurrency.toUpperCase() ]), - h('div.flex-center.confirm-memo-wrapper', [ - h('h3.confirm-screen-send-memo', [ memo ? `"${memo}"` : '' ]), - ]), - - h('div.confirm-screen-rows', [ - h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ this.context.t('from') ]), - h('div.confirm-screen-section-column', [ - h('div.confirm-screen-row-info', fromName), - h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`), - ]), - ]), - - h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ this.context.t('to') ]), - h('div.confirm-screen-section-column', [ - h('div.confirm-screen-row-info', toName), - h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`), - ]), - ]), - - h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ this.context.t('gasFee') ]), - h('div.confirm-screen-section-column', [ - h(GasFeeDisplay, { - gasTotal: gasTotal || gasFeeInHex, - conversionRate, - convertedCurrency, - onClick: () => showCustomizeGasModal(txMeta, sendGasLimit, sendGasPrice, gasTotal), - }), - ]), - ]), - - h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [ - h('div', { - className: classnames({ - 'confirm-screen-section-column--with-error': errors['insufficientFunds'], - 'confirm-screen-section-column': !errors['insufficientFunds'], - }), - }, [ - h('span.confirm-screen-label', [ this.context.t('total') + ' ' ]), - h('div.confirm-screen-total-box__subtitle', [ this.context.t('amountPlusGas') ]), - ]), - - h('div.confirm-screen-section-column', [ - h('div.confirm-screen-row-info', `${convertedTotalInFiat} ${currentCurrency.toUpperCase()}`), - h('div.confirm-screen-row-detail', `${totalInETH} ETH`), - ]), - - this.renderErrorMessage('insufficientFunds'), - ]), - ]), - -// These are latest errors handling from master -// Leaving as comments as reference when we start implementing error handling -// h('style', ` -// .conf-buttons button { -// margin-left: 10px; -// text-transform: uppercase; -// } -// `), - -// txMeta.simulationFails ? -// h('.error', { -// style: { -// marginLeft: 50, -// fontSize: '0.9em', -// }, -// }, 'Transaction Error. Exception thrown in contract code.') -// : null, - -// !isValidAddress ? -// h('.error', { -// style: { -// marginLeft: 50, -// fontSize: '0.9em', -// }, -// }, 'Recipient address is invalid. Sending this transaction will result in a loss of ETH.') -// : null, - -// insufficientBalance ? -// h('span.error', { -// style: { -// marginLeft: 50, -// fontSize: '0.9em', -// }, -// }, 'Insufficient balance for transaction') -// : null, - -// // send + cancel -// h('.flex-row.flex-space-around.conf-buttons', { -// style: { -// display: 'flex', -// justifyContent: 'flex-end', -// margin: '14px 25px', -// }, -// }, [ -// h('button', { -// onClick: (event) => { -// this.resetGasFields() -// event.preventDefault() -// }, -// }, 'Reset'), - -// // Accept Button or Buy Button -// insufficientBalance ? h('button.btn-green', { onClick: props.buyEth }, 'Buy Ether') : -// h('input.confirm.btn-green', { -// type: 'submit', -// value: 'SUBMIT', -// style: { marginLeft: '10px' }, -// disabled: buyDisabled, -// }), - -// h('button.cancel.btn-red', { -// onClick: props.cancelTransaction, -// }, 'Reject'), -// ]), -// showRejectAll ? h('.flex-row.flex-space-around.conf-buttons', { -// style: { -// display: 'flex', -// justifyContent: 'flex-end', -// margin: '14px 25px', -// }, -// }, [ -// h('button.cancel.btn-red', { -// onClick: props.cancelAllTransactions, -// }, 'Reject All'), -// ]) : null, -// ]), -// ]) -// ) -// } - ]), - - h('form#pending-tx-form', { - className: 'confirm-screen-form', - onSubmit: this.onSubmit, - }, [ - this.renderErrorMessage('simulationFails'), - h('.page-container__footer', [ - // Cancel Button - h('button.btn-cancel.page-container__footer-button.allcaps', { - onClick: (event) => { - clearSend() - this.cancel(event, txMeta) - }, - }, this.context.t('cancel')), - - // Accept Button - h('button.btn-confirm.page-container__footer-button.allcaps', { - onClick: event => this.onSubmit(event), - }, this.context.t('confirm')), - ]), - ]), - ]) - ) -} - -ConfirmSendEther.prototype.renderErrorMessage = function (message) { - const { send: { errors } } = this.props - - return errors[message] - ? h('div.confirm-screen-error', [ errors[message] ]) - : null -} - -ConfirmSendEther.prototype.onSubmit = function (event) { - event.preventDefault() - const { updateSendErrors } = this.props - const txMeta = this.gatherTxMeta() - const valid = this.checkValidity() - const balanceIsSufficient = this.isBalanceSufficient(txMeta) - this.setState({ valid, submitting: true }) - - if (valid && this.verifyGasParams() && balanceIsSufficient) { - this.props.sendTransaction(txMeta, event) - } else if (!balanceIsSufficient) { - updateSendErrors({ insufficientFunds: 'insufficientFunds' }) - } else { - updateSendErrors({ invalidGasParams: 'invalidGasParams' }) - this.setState({ submitting: false }) - } -} - -ConfirmSendEther.prototype.cancel = function (event, txMeta) { - event.preventDefault() - const { cancelTransaction } = this.props - - cancelTransaction(txMeta) - .then(() => this.props.history.push(DEFAULT_ROUTE)) -} - -ConfirmSendEther.prototype.isBalanceSufficient = function (txMeta) { - const { - balance, - conversionRate, - } = this.props - const { - txParams: { - gas, - gasPrice, - value: amount, - }, - } = txMeta - const gasTotal = calcGasTotal(gas, gasPrice) - - return isBalanceSufficient({ - amount, - gasTotal, - balance, - conversionRate, - }) -} - -ConfirmSendEther.prototype.checkValidity = function () { - const form = this.getFormEl() - const valid = form.checkValidity() - return valid -} - -ConfirmSendEther.prototype.getFormEl = function () { - const form = document.querySelector('form#pending-tx-form') - // Stub out form for unit tests: - if (!form) { - return { checkValidity () { return true } } - } - return form -} - -// After a customizable state value has been updated, -ConfirmSendEther.prototype.gatherTxMeta = function () { - const props = this.props - const state = this.state - const txData = clone(state.txData) || clone(props.txData) - - const { gasPrice: sendGasPrice, gasLimit: sendGasLimit } = props.send - const { - lastGasPrice, - txParams: { - gasPrice: txGasPrice, - gas: txGasLimit, - }, - } = txData - - let forceGasMin - if (lastGasPrice) { - forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, { - multiplicandBase: 16, - multiplierBase: 10, - toNumericBase: 'hex', - })) - } - - txData.txParams.gasPrice = sendGasPrice || forceGasMin || txGasPrice - txData.txParams.gas = sendGasLimit || txGasLimit - - // log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`) - return txData -} - -ConfirmSendEther.prototype.verifyGasParams = function () { - // We call this in case the gas has not been modified at all - if (!this.state) { return true } - return ( - this._notZeroOrEmptyString(this.state.gas) && - this._notZeroOrEmptyString(this.state.gasPrice) - ) -} - -ConfirmSendEther.prototype._notZeroOrEmptyString = function (obj) { - return obj !== '' && obj !== '0x0' -} - -ConfirmSendEther.prototype.bnMultiplyByFraction = function (targetBN, numerator, denominator) { - const numBN = new BN(numerator) - const denomBN = new BN(denominator) - return targetBN.mul(numBN).div(denomBN) -} diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js deleted file mode 100644 index 535347cee..000000000 --- a/ui/app/components/pending-tx/confirm-send-token.js +++ /dev/null @@ -1,696 +0,0 @@ -const Component = require('react').Component -const { withRouter } = require('react-router-dom') -const { compose } = require('recompose') -const PropTypes = require('prop-types') -const connect = require('react-redux').connect -const h = require('react-hyperscript') -const inherits = require('util').inherits -const tokenAbi = require('human-standard-token-abi') -const abiDecoder = require('abi-decoder') -abiDecoder.addABI(tokenAbi) -const actions = require('../../actions') -const clone = require('clone') -const Identicon = require('../identicon') -const GasFeeDisplay = require('../send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js').default -const NetworkDisplay = require('../network-display') -const ethUtil = require('ethereumjs-util') -const BN = ethUtil.BN -const { - conversionUtil, - multiplyCurrencies, - addCurrencies, -} = require('../../conversion-util') -const { - calcGasTotal, - isBalanceSufficient, -} = require('../send_/send.utils') -const { - calcTokenAmount, -} = require('../../token-util') -const classnames = require('classnames') -const currencyFormatter = require('currency-formatter') -const currencies = require('currency-formatter/currencies') - -const { MIN_GAS_PRICE_HEX } = require('../send_/send.constants') - -const { - getTokenExchangeRate, - getSelectedAddress, - getSelectedTokenContract, -} = require('../../selectors') -const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes') - -import { - updateSendErrors, -} from '../../ducks/send.duck' - -const { - ENVIRONMENT_TYPE_POPUP, - ENVIRONMENT_TYPE_NOTIFICATION, -} = require('../../../../app/scripts/lib/enums') - -ConfirmSendToken.contextTypes = { - t: PropTypes.func, -} - -module.exports = compose( - withRouter, - connect(mapStateToProps, mapDispatchToProps) -)(ConfirmSendToken) - - -function mapStateToProps (state, ownProps) { - const { token: { address }, txData } = ownProps - const { txParams } = txData || {} - const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data) - - const { - conversionRate, - identities, - currentCurrency, - } = state.metamask - const accounts = state.metamask.accounts - const selectedAddress = getSelectedAddress(state) - const tokenExchangeRate = getTokenExchangeRate(state, address) - const { balance } = accounts[selectedAddress] - return { - conversionRate, - identities, - selectedAddress, - tokenExchangeRate, - tokenData: tokenData || {}, - currentCurrency: currentCurrency.toUpperCase(), - send: state.metamask.send, - tokenContract: getSelectedTokenContract(state), - balance, - } -} - -function mapDispatchToProps (dispatch, ownProps) { - return { - backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)), - cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })), - editTransaction: txMeta => { - const { token: { address } } = ownProps - const { txParams = {}, id } = txMeta - const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data) || {} - const { params = [] } = tokenData - const { value: to } = params[0] || {} - const { value: tokenAmountInDec } = params[1] || {} - const tokenAmountInHex = conversionUtil(tokenAmountInDec, { - fromNumericBase: 'dec', - toNumericBase: 'hex', - }) - const { - gas: gasLimit, - gasPrice, - } = txParams - dispatch(actions.setSelectedToken(address)) - dispatch(actions.updateSend({ - gasLimit, - gasPrice, - gasTotal: null, - to, - amount: tokenAmountInHex, - errors: { to: null, amount: null }, - editingTransactionId: id && id.toString(), - token: ownProps.token, - })) - dispatch(actions.showSendTokenPage()) - }, - showCustomizeGasModal: (txMeta, sendGasLimit, sendGasPrice, sendGasTotal) => { - const { id, txParams, lastGasPrice } = txMeta - const { gas: txGasLimit, gasPrice: txGasPrice } = txParams - const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data) - const { params = [] } = tokenData - const { value: to } = params[0] || {} - const { value: tokenAmountInDec } = params[1] || {} - const tokenAmountInHex = conversionUtil(tokenAmountInDec, { - fromNumericBase: 'dec', - toNumericBase: 'hex', - }) - - let forceGasMin - if (lastGasPrice) { - forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, { - multiplicandBase: 16, - multiplierBase: 10, - toNumericBase: 'hex', - fromDenomination: 'WEI', - })) - } - - dispatch(actions.updateSend({ - gasLimit: sendGasLimit || txGasLimit, - gasPrice: sendGasPrice || txGasPrice, - editingTransactionId: id, - gasTotal: sendGasTotal, - to, - amount: tokenAmountInHex, - forceGasMin, - })) - dispatch(actions.showModal({ name: 'CUSTOMIZE_GAS' })) - }, - updateSendErrors: error => dispatch(updateSendErrors(error)), - } -} - -inherits(ConfirmSendToken, Component) -function ConfirmSendToken () { - Component.call(this) - this.state = {} - this.onSubmit = this.onSubmit.bind(this) -} - -ConfirmSendToken.prototype.editTransaction = function (txMeta) { - const { editTransaction, history } = this.props - editTransaction(txMeta) - history.push(SEND_ROUTE) -} - -ConfirmSendToken.prototype.updateComponentSendErrors = function (prevProps) { - const { - balance: oldBalance, - conversionRate: oldConversionRate, - } = prevProps - const { - updateSendErrors, - balance, - conversionRate, - send: { - errors: { - simulationFails, - }, - }, - } = this.props - const txMeta = this.gatherTxMeta() - - const shouldUpdateBalanceSendErrors = balance && [ - balance !== oldBalance, - conversionRate !== oldConversionRate, - ].some(x => Boolean(x)) - - if (shouldUpdateBalanceSendErrors) { - const balanceIsSufficient = this.isBalanceSufficient(txMeta) - updateSendErrors({ - insufficientFunds: balanceIsSufficient ? false : this.context.t('insufficientFunds'), - }) - } - - const shouldUpdateSimulationSendError = Boolean(txMeta.simulationFails) !== Boolean(simulationFails) - - if (shouldUpdateSimulationSendError) { - updateSendErrors({ - simulationFails: !txMeta.simulationFails ? false : this.context.t('transactionError'), - }) - } -} - -ConfirmSendToken.prototype.componentWillMount = function () { - const { tokenContract, selectedAddress } = this.props - tokenContract && tokenContract - .balanceOf(selectedAddress) - .then(usersToken => { - }) - this.updateComponentSendErrors({}) -} - -ConfirmSendToken.prototype.componentDidUpdate = function (prevProps) { - this.updateComponentSendErrors(prevProps) -} - -ConfirmSendToken.prototype.getAmount = function () { - const { - conversionRate, - tokenExchangeRate, - token, - tokenData, - send: { amount, editingTransactionId }, - } = this.props - const { params = [] } = tokenData - let { value } = params[1] || {} - const { decimals } = token - - if (editingTransactionId) { - value = conversionUtil(amount, { - fromNumericBase: 'hex', - toNumericBase: 'dec', - }) - } - - const sendTokenAmount = calcTokenAmount(value, decimals) - - return { - fiat: tokenExchangeRate - ? +(sendTokenAmount * tokenExchangeRate * conversionRate).toFixed(2) - : null, - token: typeof value === 'undefined' - ? this.context.t('unknown') - : +sendTokenAmount.toFixed(decimals), - } - -} - -ConfirmSendToken.prototype.getGasFee = function () { - const { conversionRate, tokenExchangeRate, token, currentCurrency } = this.props - const txMeta = this.gatherTxMeta() - const txParams = txMeta.txParams || {} - const { decimals } = token - - const gas = txParams.gas - const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_HEX - const gasTotal = multiplyCurrencies(gas, gasPrice, { - multiplicandBase: 16, - multiplierBase: 16, - }) - - const FIAT = conversionUtil(gasTotal, { - fromNumericBase: 'BN', - toNumericBase: 'dec', - fromDenomination: 'WEI', - fromCurrency: 'ETH', - toCurrency: currentCurrency, - numberOfDecimals: 2, - conversionRate, - }) - const ETH = conversionUtil(gasTotal, { - fromNumericBase: 'BN', - toNumericBase: 'dec', - fromDenomination: 'WEI', - fromCurrency: 'ETH', - toCurrency: 'ETH', - numberOfDecimals: 6, - conversionRate, - }) - const tokenGas = multiplyCurrencies(gas, gasPrice, { - toNumericBase: 'dec', - multiplicandBase: 16, - multiplierBase: 16, - toCurrency: 'BAT', - conversionRate: tokenExchangeRate, - invertConversionRate: true, - fromDenomination: 'WEI', - numberOfDecimals: decimals || 4, - }) - - return { - fiat: +Number(FIAT).toFixed(2), - eth: ETH, - token: tokenExchangeRate - ? tokenGas - : null, - gasFeeInHex: gasTotal.toString(16), - } -} - -ConfirmSendToken.prototype.getData = function () { - const { identities, tokenData } = this.props - const { params = [] } = tokenData - const { value } = params[0] || {} - const txMeta = this.gatherTxMeta() - const txParams = txMeta.txParams || {} - - return { - from: { - address: txParams.from, - name: identities[txParams.from].name, - }, - to: { - address: value, - name: identities[value] ? identities[value].name : this.context.t('newRecipient'), - }, - memo: txParams.memo || '', - } -} - -ConfirmSendToken.prototype.renderHeroAmount = function () { - const { token: { symbol }, currentCurrency } = this.props - const { fiat: fiatAmount, token: tokenAmount } = this.getAmount() - const txMeta = this.gatherTxMeta() - const txParams = txMeta.txParams || {} - const { memo = '' } = txParams - - const convertedAmountInFiat = this.convertToRenderableCurrency(fiatAmount, currentCurrency) - - return fiatAmount - ? ( - h('div.confirm-send-token__hero-amount-wrapper', [ - h('h3.flex-center.confirm-screen-send-amount', `${convertedAmountInFiat}`), - h('h3.flex-center.confirm-screen-send-amount-currency', currentCurrency), - h('div.flex-center.confirm-memo-wrapper', [ - h('h3.confirm-screen-send-memo', [ memo ? `"${memo}"` : '' ]), - ]), - ]) - ) - : ( - h('div.confirm-send-token__hero-amount-wrapper', [ - h('h3.flex-center.confirm-screen-send-amount', tokenAmount), - h('h3.flex-center.confirm-screen-send-amount-currency', symbol), - h('div.flex-center.confirm-memo-wrapper', [ - h('h3.confirm-screen-send-memo', [ memo ? `"${memo}"` : '' ]), - ]), - ]) - ) -} - -ConfirmSendToken.prototype.renderGasFee = function () { - const { - currentCurrency: convertedCurrency, - conversionRate, - send: { gasTotal, gasLimit: sendGasLimit, gasPrice: sendGasPrice }, - showCustomizeGasModal, - } = this.props - const txMeta = this.gatherTxMeta() - const { gasFeeInHex } = this.getGasFee() - - return ( - h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ this.context.t('gasFee') ]), - h('div.confirm-screen-section-column', [ - h(GasFeeDisplay, { - gasTotal: gasTotal || gasFeeInHex, - conversionRate, - convertedCurrency, - onClick: () => showCustomizeGasModal(txMeta, sendGasLimit, sendGasPrice, gasTotal), - }), - ]), - ]) - ) -} - -ConfirmSendToken.prototype.renderTotalPlusGas = function () { - const { token: { symbol }, currentCurrency, send: { errors } } = this.props - const { fiat: fiatAmount, token: tokenAmount } = this.getAmount() - const { fiat: fiatGas, token: tokenGas } = this.getGasFee() - - const totalInFIAT = fiatAmount && fiatGas && addCurrencies(fiatAmount, fiatGas) - const convertedTotalInFiat = this.convertToRenderableCurrency(totalInFIAT, currentCurrency) - - return fiatAmount && fiatGas - ? ( - h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [ - h('div.confirm-screen-section-column', [ - h('span.confirm-screen-label', [ this.context.t('total') + ' ' ]), - h('div.confirm-screen-total-box__subtitle', [ this.context.t('amountPlusGas') ]), - ]), - - h('div.confirm-screen-section-column', [ - h('div.confirm-screen-row-info', `${convertedTotalInFiat} ${currentCurrency}`), - h('div.confirm-screen-row-detail', `${addCurrencies(tokenAmount, tokenGas || '0')} ${symbol}`), - ]), - ]) - ) - : ( - h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [ - h('div', { - className: classnames({ - 'confirm-screen-section-column--with-error': errors['insufficientFunds'], - 'confirm-screen-section-column': !errors['insufficientFunds'], - }), - }, [ - h('span.confirm-screen-label', [ this.context.t('total') + ' ' ]), - h('div.confirm-screen-total-box__subtitle', [ this.context.t('amountPlusGas') ]), - ]), - - h('div.confirm-screen-section-column', [ - h('div.confirm-screen-row-info', `${tokenAmount} ${symbol}`), - h('div.confirm-screen-row-detail', `+ ${fiatGas} ${currentCurrency} ${this.context.t('gas')}`), - ]), - - this.renderErrorMessage('insufficientFunds'), - ]) - ) -} - -ConfirmSendToken.prototype.renderErrorMessage = function (message) { - const { send: { errors } } = this.props - - return errors[message] - ? h('div.confirm-screen-error', [ errors[message] ]) - : null -} - -ConfirmSendToken.prototype.convertToRenderableCurrency = function (value, currencyCode) { - const upperCaseCurrencyCode = currencyCode.toUpperCase() - - return currencies.find(currency => currency.code === upperCaseCurrencyCode) - ? currencyFormatter.format(Number(value), { - code: upperCaseCurrencyCode, - }) - : value -} - -ConfirmSendToken.prototype.renderHeaderRow = function (isTxReprice) { - const windowType = window.METAMASK_UI_TYPE - const isFullScreen = windowType !== ENVIRONMENT_TYPE_NOTIFICATION && - windowType !== ENVIRONMENT_TYPE_POPUP - - if (isTxReprice && isFullScreen) { - return null - } - - return ( - h('.page-container__header-row', [ - h('span.page-container__back-button', { - onClick: () => this.editTransaction(), - style: { - visibility: isTxReprice ? 'hidden' : 'initial', - }, - }, 'Edit'), - !isFullScreen && h(NetworkDisplay), - ]) - ) -} - -ConfirmSendToken.prototype.renderHeader = function (isTxReprice) { - const title = isTxReprice ? this.context.t('speedUpTitle') : this.context.t('confirm') - const subtitle = isTxReprice - ? this.context.t('speedUpSubtitle') - : this.context.t('pleaseReviewTransaction') - - return ( - h('.page-container__header', [ - this.renderHeaderRow(isTxReprice), - h('.page-container__title', title), - h('.page-container__subtitle', subtitle), - ]) - ) -} - -ConfirmSendToken.prototype.render = function () { - const txMeta = this.gatherTxMeta() - const { - from: { - address: fromAddress, - name: fromName, - }, - to: { - address: toAddress, - name: toName, - }, - } = this.getData() - - const isTxReprice = Boolean(txMeta.lastGasPrice) - - return ( - h('div.confirm-screen-container.confirm-send-token', [ - // Main Send token Card - h('div.page-container', [ - this.renderHeader(isTxReprice), - h('.page-container__content', [ - h('div.flex-row.flex-center.confirm-screen-identicons', [ - h('div.confirm-screen-account-wrapper', [ - h( - Identicon, - { - address: fromAddress, - diameter: 60, - }, - ), - h('span.confirm-screen-account-name', fromName), - // h('span.confirm-screen-account-number', fromAddress.slice(fromAddress.length - 4)), - ]), - h('i.fa.fa-arrow-right.fa-lg'), - h('div.confirm-screen-account-wrapper', [ - h( - Identicon, - { - address: toAddress, - diameter: 60, - }, - ), - h('span.confirm-screen-account-name', toName), - // h('span.confirm-screen-account-number', toAddress.slice(toAddress.length - 4)), - ]), - ]), - - // h('h3.flex-center.confirm-screen-sending-to-message', { - // style: { - // textAlign: 'center', - // fontSize: '16px', - // }, - // }, [ - // `You're sending to Recipient ...${toAddress.slice(toAddress.length - 4)}`, - // ]), - - this.renderHeroAmount(), - - h('div.confirm-screen-rows', [ - h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ this.context.t('from') ]), - h('div.confirm-screen-section-column', [ - h('div.confirm-screen-row-info', fromName), - h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`), - ]), - ]), - - toAddress && h('section.flex-row.flex-center.confirm-screen-row', [ - h('span.confirm-screen-label.confirm-screen-section-column', [ this.context.t('to') ]), - h('div.confirm-screen-section-column', [ - h('div.confirm-screen-row-info', toName), - h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`), - ]), - ]), - - this.renderGasFee(), - - this.renderTotalPlusGas(), - - ]), - - ]), - - h('form#pending-tx-form', { - className: 'confirm-screen-form', - onSubmit: this.onSubmit, - }, [ - this.renderErrorMessage('simulationFails'), - h('.page-container__footer', [ - // Cancel Button - h('button.btn-cancel.page-container__footer-button.allcaps', { - onClick: (event) => this.cancel(event, txMeta), - }, this.context.t('cancel')), - - // Accept Button - h('button.btn-confirm.page-container__footer-button.allcaps', { - onClick: event => this.onSubmit(event), - }, [this.context.t('confirm')]), - ]), - ]), - ]), - ]) - ) -} - -ConfirmSendToken.prototype.onSubmit = function (event) { - event.preventDefault() - const { updateSendErrors } = this.props - const txMeta = this.gatherTxMeta() - const valid = this.checkValidity() - const balanceIsSufficient = this.isBalanceSufficient(txMeta) - this.setState({ valid, submitting: true }) - - if (valid && this.verifyGasParams() && balanceIsSufficient) { - this.props.sendTransaction(txMeta, event) - } else if (!balanceIsSufficient) { - updateSendErrors({ insufficientFunds: 'insufficientFunds' }) - } else { - updateSendErrors({ invalidGasParams: 'invalidGasParams' }) - this.setState({ submitting: false }) - } -} - -ConfirmSendToken.prototype.isBalanceSufficient = function (txMeta) { - const { - balance, - conversionRate, - } = this.props - const { - txParams: { - gas, - gasPrice, - }, - } = txMeta - const gasTotal = calcGasTotal(gas, gasPrice) - - return isBalanceSufficient({ - amount: '0', - gasTotal, - balance, - conversionRate, - }) -} - - -ConfirmSendToken.prototype.cancel = function (event, txMeta) { - event.preventDefault() - const { cancelTransaction } = this.props - - cancelTransaction(txMeta) - .then(() => this.props.history.push(DEFAULT_ROUTE)) -} - -ConfirmSendToken.prototype.checkValidity = function () { - const form = this.getFormEl() - const valid = form.checkValidity() - return valid -} - -ConfirmSendToken.prototype.getFormEl = function () { - const form = document.querySelector('form#pending-tx-form') - // Stub out form for unit tests: - if (!form) { - return { checkValidity () { return true } } - } - return form -} - -// After a customizable state value has been updated, -ConfirmSendToken.prototype.gatherTxMeta = function () { - const props = this.props - const state = this.state - const txData = clone(state.txData) || clone(props.txData) - - const { gasPrice: sendGasPrice, gasLimit: sendGasLimit } = props.send - const { - lastGasPrice, - txParams: { - gasPrice: txGasPrice, - gas: txGasLimit, - }, - } = txData - - let forceGasMin - if (lastGasPrice) { - forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, { - multiplicandBase: 16, - multiplierBase: 10, - toNumericBase: 'hex', - })) - } - - txData.txParams.gasPrice = sendGasPrice || forceGasMin || txGasPrice - txData.txParams.gas = sendGasLimit || txGasLimit - - // log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`) - return txData -} - -ConfirmSendToken.prototype.verifyGasParams = function () { - // We call this in case the gas has not been modified at all - if (!this.state) { return true } - return ( - this._notZeroOrEmptyString(this.state.gas) && - this._notZeroOrEmptyString(this.state.gasPrice) - ) -} - -ConfirmSendToken.prototype._notZeroOrEmptyString = function (obj) { - return obj !== '' && obj !== '0x0' -} - -ConfirmSendToken.prototype.bnMultiplyByFraction = function (targetBN, numerator, denominator) { - const numBN = new BN(numerator) - const denomBN = new BN(denominator) - return targetBN.mul(numBN).div(denomBN) -} diff --git a/ui/app/components/pending-tx/index.js b/ui/app/components/pending-tx/index.js deleted file mode 100644 index 3f8cd8823..000000000 --- a/ui/app/components/pending-tx/index.js +++ /dev/null @@ -1,165 +0,0 @@ -const Component = require('react').Component -const connect = require('react-redux').connect -const h = require('react-hyperscript') -const PropTypes = require('prop-types') -const clone = require('clone') -const abi = require('human-standard-token-abi') -const abiDecoder = require('abi-decoder') -abiDecoder.addABI(abi) -const inherits = require('util').inherits -const actions = require('../../actions') -const { getSymbolAndDecimals } = require('../../token-util') -const ConfirmSendEther = require('./confirm-send-ether') -const ConfirmSendToken = require('./confirm-send-token') -const ConfirmDeployContract = require('./confirm-deploy-contract') -const Loading = require('../loading-screen') - -const TX_TYPES = { - DEPLOY_CONTRACT: 'deploy_contract', - SEND_ETHER: 'send_ether', - SEND_TOKEN: 'send_token', -} - -module.exports = connect(mapStateToProps, mapDispatchToProps)(PendingTx) - -function mapStateToProps (state) { - const { - conversionRate, - identities, - tokens: existingTokens, - } = state.metamask - const accounts = state.metamask.accounts - const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0] - return { - conversionRate, - identities, - selectedAddress, - existingTokens, - } -} - -function mapDispatchToProps (dispatch) { - return { - backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)), - cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })), - } -} - -inherits(PendingTx, Component) -function PendingTx () { - Component.call(this) - this.state = { - isFetching: true, - transactionType: '', - tokenAddress: '', - tokenSymbol: '', - tokenDecimals: '', - } -} - -PendingTx.prototype.componentDidMount = function () { - this.setTokenData() -} - -PendingTx.prototype.componentDidUpdate = function (prevProps, prevState) { - if (prevState.isFetching) { - this.setTokenData() - } -} - -PendingTx.prototype.setTokenData = async function () { - const { existingTokens } = this.props - const txMeta = this.gatherTxMeta() - const txParams = txMeta.txParams || {} - - if (txMeta.loadingDefaults) { - return - } - - if (!txParams.to) { - return this.setState({ - transactionType: TX_TYPES.DEPLOY_CONTRACT, - isFetching: false, - }) - } - - // inspect tx data for supported special confirmation screens - let isTokenTransaction = false - if (txParams.data) { - const tokenData = abiDecoder.decodeMethod(txParams.data) - const { name: tokenMethodName } = tokenData || {} - isTokenTransaction = (tokenMethodName === 'transfer') - } - - if (isTokenTransaction) { - const { symbol, decimals } = await getSymbolAndDecimals(txParams.to, existingTokens) - - this.setState({ - transactionType: TX_TYPES.SEND_TOKEN, - tokenAddress: txParams.to, - tokenSymbol: symbol, - tokenDecimals: decimals, - isFetching: false, - }) - } else { - this.setState({ - transactionType: TX_TYPES.SEND_ETHER, - isFetching: false, - }) - } -} - -PendingTx.prototype.gatherTxMeta = function () { - const props = this.props - const state = this.state - const txData = clone(state.txData) || clone(props.txData) - - return txData -} - -PendingTx.prototype.render = function () { - const { - isFetching, - transactionType, - tokenAddress, - tokenSymbol, - tokenDecimals, - } = this.state - - const { sendTransaction } = this.props - - if (isFetching) { - return h(Loading, { - loadingMessage: this.context.t('generatingTransaction'), - }) - } - - switch (transactionType) { - case TX_TYPES.SEND_ETHER: - return h(ConfirmSendEther, { - txData: this.gatherTxMeta(), - sendTransaction, - }) - case TX_TYPES.SEND_TOKEN: - return h(ConfirmSendToken, { - txData: this.gatherTxMeta(), - sendTransaction, - token: { - address: tokenAddress, - symbol: tokenSymbol, - decimals: tokenDecimals, - }, - }) - case TX_TYPES.DEPLOY_CONTRACT: - return h(ConfirmDeployContract, { - txData: this.gatherTxMeta(), - sendTransaction, - }) - default: - return h(Loading) - } -} - -PendingTx.contextTypes = { - t: PropTypes.func, -} diff --git a/ui/app/components/selected-account/selected-account.component.js b/ui/app/components/selected-account/selected-account.component.js index 3386a4196..6c202141e 100644 --- a/ui/app/components/selected-account/selected-account.component.js +++ b/ui/app/components/selected-account/selected-account.component.js @@ -1,17 +1,10 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import copyToClipboard from 'copy-to-clipboard' +import { addressSlicer } from '../../util' const Tooltip = require('../tooltip-v2.js') -const addressStripper = (address = '') => { - if (address.length < 4) { - return address - } - - return `${address.slice(0, 4)}...${address.slice(-4)}` -} - class SelectedAccount extends Component { state = { copied: false, @@ -48,7 +41,7 @@ class SelectedAccount extends Component { { selectedIdentity.name } </div> <div className="selected-account__address"> - { addressStripper(selectedAddress) } + { addressSlicer(selectedAddress) } </div> </div> </Tooltip> diff --git a/ui/app/components/send_/README.md b/ui/app/components/send/README.md index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/README.md +++ b/ui/app/components/send/README.md diff --git a/ui/app/components/send_/account-list-item/account-list-item-README.md b/ui/app/components/send/account-list-item/account-list-item-README.md index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/account-list-item/account-list-item-README.md +++ b/ui/app/components/send/account-list-item/account-list-item-README.md diff --git a/ui/app/components/send_/account-list-item/account-list-item.component.js b/ui/app/components/send/account-list-item/account-list-item.component.js index 322246f61..9f4a96e61 100644 --- a/ui/app/components/send_/account-list-item/account-list-item.component.js +++ b/ui/app/components/send/account-list-item/account-list-item.component.js @@ -2,7 +2,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { checksumAddress } from '../../../util' import Identicon from '../../identicon' -import CurrencyDisplay from '../../send/currency-display' +import CurrencyDisplay from '../currency-display' export default class AccountListItem extends Component { diff --git a/ui/app/components/send_/account-list-item/account-list-item.container.js b/ui/app/components/send/account-list-item/account-list-item.container.js index 4b4519288..4b4519288 100644 --- a/ui/app/components/send_/account-list-item/account-list-item.container.js +++ b/ui/app/components/send/account-list-item/account-list-item.container.js diff --git a/ui/app/components/send_/account-list-item/account-list-item.scss b/ui/app/components/send/account-list-item/account-list-item.scss index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/account-list-item/account-list-item.scss +++ b/ui/app/components/send/account-list-item/account-list-item.scss diff --git a/ui/app/components/send_/account-list-item/index.js b/ui/app/components/send/account-list-item/index.js index 907485cf7..907485cf7 100644 --- a/ui/app/components/send_/account-list-item/index.js +++ b/ui/app/components/send/account-list-item/index.js diff --git a/ui/app/components/send_/account-list-item/tests/account-list-item-component.test.js b/ui/app/components/send/account-list-item/tests/account-list-item-component.test.js index bb7f3776c..ef152d2e7 100644 --- a/ui/app/components/send_/account-list-item/tests/account-list-item-component.test.js +++ b/ui/app/components/send/account-list-item/tests/account-list-item-component.test.js @@ -4,7 +4,7 @@ import { shallow } from 'enzyme' import sinon from 'sinon' import proxyquire from 'proxyquire' import Identicon from '../../../identicon' -import CurrencyDisplay from '../../../send/currency-display' +import CurrencyDisplay from '../../currency-display' const utilsMethodStubs = { checksumAddress: sinon.stub().returns('mockCheckSumAddress'), diff --git a/ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js b/ui/app/components/send/account-list-item/tests/account-list-item-container.test.js index af0859117..af0859117 100644 --- a/ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js +++ b/ui/app/components/send/account-list-item/tests/account-list-item-container.test.js diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display/currency-display.js index 1cf55ce1a..2b8eaa41f 100644 --- a/ui/app/components/send/currency-display.js +++ b/ui/app/components/send/currency-display/currency-display.js @@ -1,11 +1,16 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits -const { conversionUtil, multiplyCurrencies } = require('../../conversion-util') -const { removeLeadingZeroes } = require('../send_/send.utils') +const { conversionUtil, multiplyCurrencies } = require('../../../conversion-util') +const { removeLeadingZeroes } = require('../send.utils') const currencyFormatter = require('currency-formatter') const currencies = require('currency-formatter/currencies') const ethUtil = require('ethereumjs-util') +const PropTypes = require('prop-types') + +CurrencyDisplay.contextTypes = { + t: PropTypes.func, +} module.exports = CurrencyDisplay @@ -75,6 +80,12 @@ CurrencyDisplay.prototype.getValueToRender = function ({ selectedToken, conversi CurrencyDisplay.prototype.getConvertedValueToRender = function (nonFormattedValue) { const { primaryCurrency, convertedCurrency, conversionRate } = this.props + if (conversionRate === 0 || conversionRate === null || conversionRate === undefined) { + if (nonFormattedValue !== 0) { + return null + } + } + let convertedValue = conversionUtil(nonFormattedValue, { fromNumericBase: 'dec', fromCurrency: primaryCurrency, @@ -82,16 +93,15 @@ CurrencyDisplay.prototype.getConvertedValueToRender = function (nonFormattedValu numberOfDecimals: 2, conversionRate, }) - convertedValue = Number(convertedValue).toFixed(2) + convertedValue = Number(convertedValue).toFixed(2) const upperCaseCurrencyCode = convertedCurrency.toUpperCase() - return currencies.find(currency => currency.code === upperCaseCurrencyCode) ? currencyFormatter.format(Number(convertedValue), { code: upperCaseCurrencyCode, }) - : convertedValue -} + : convertedValue + } CurrencyDisplay.prototype.handleChange = function (newVal) { this.setState({ valueToRender: removeLeadingZeroes(newVal) }) @@ -105,13 +115,24 @@ CurrencyDisplay.prototype.getInputWidth = function (valueToRender, readOnly) { return (valueLength + decimalPointDeficit + 0.75) + 'ch' } +CurrencyDisplay.prototype.onlyRenderConversions = function (convertedValueToRender) { + const { + convertedBalanceClassName = 'currency-display__converted-value', + convertedCurrency, + } = this.props + return h('div', { + className: convertedBalanceClassName, + }, convertedValueToRender == null + ? this.context.t('noConversionRateAvailable') + : `${convertedValueToRender} ${convertedCurrency.toUpperCase()}` +) + } + CurrencyDisplay.prototype.render = function () { const { className = 'currency-display', primaryBalanceClassName = 'currency-display__input', - convertedBalanceClassName = 'currency-display__converted-value', primaryCurrency, - convertedCurrency, readOnly = false, inError = false, onBlur, @@ -157,11 +178,7 @@ CurrencyDisplay.prototype.render = function () { ]), - ]), - - h('div', { - className: convertedBalanceClassName, - }, `${convertedValueToRender} ${convertedCurrency.toUpperCase()}`), + ]), this.onlyRenderConversions(convertedValueToRender), ]) diff --git a/ui/app/components/send/currency-display/index.js b/ui/app/components/send/currency-display/index.js new file mode 100644 index 000000000..5dc269c5a --- /dev/null +++ b/ui/app/components/send/currency-display/index.js @@ -0,0 +1 @@ +export { default } from './currency-display.js' diff --git a/ui/app/components/send_/index.js b/ui/app/components/send/index.js index b5114babc..b5114babc 100644 --- a/ui/app/components/send_/index.js +++ b/ui/app/components/send/index.js diff --git a/ui/app/components/send_/send-content/index.js b/ui/app/components/send/send-content/index.js index 891c17e6a..891c17e6a 100644 --- a/ui/app/components/send_/send-content/index.js +++ b/ui/app/components/send/send-content/index.js diff --git a/ui/app/components/send_/send-content/send-amount-row/README.md b/ui/app/components/send/send-content/send-amount-row/README.md index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-amount-row/README.md +++ b/ui/app/components/send/send-content/send-amount-row/README.md diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.component.js b/ui/app/components/send/send-content/send-amount-row/amount-max-button/amount-max-button.component.js index 4d0d36ab4..4d0d36ab4 100644 --- a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.component.js +++ b/ui/app/components/send/send-content/send-amount-row/amount-max-button/amount-max-button.component.js diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.container.js b/ui/app/components/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js index 2d2ec42f7..2d2ec42f7 100644 --- a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.container.js +++ b/ui/app/components/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.selectors.js b/ui/app/components/send/send-content/send-amount-row/amount-max-button/amount-max-button.selectors.js index 69fec1994..69fec1994 100644 --- a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.selectors.js +++ b/ui/app/components/send/send-content/send-amount-row/amount-max-button/amount-max-button.selectors.js diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.utils.js b/ui/app/components/send/send-content/send-amount-row/amount-max-button/amount-max-button.utils.js index b490a7fd7..b490a7fd7 100644 --- a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.utils.js +++ b/ui/app/components/send/send-content/send-amount-row/amount-max-button/amount-max-button.utils.js diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/index.js b/ui/app/components/send/send-content/send-amount-row/amount-max-button/index.js index ee8271494..ee8271494 100644 --- a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/index.js +++ b/ui/app/components/send/send-content/send-amount-row/amount-max-button/index.js diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js b/ui/app/components/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js index 86a05ff21..86a05ff21 100644 --- a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js +++ b/ui/app/components/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js b/ui/app/components/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js index 2cc00d6d6..2cc00d6d6 100644 --- a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js +++ b/ui/app/components/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-selectors.test.js b/ui/app/components/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-selectors.test.js index 655fe1969..655fe1969 100644 --- a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-selectors.test.js +++ b/ui/app/components/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-selectors.test.js diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-utils.test.js b/ui/app/components/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-utils.test.js index 816df6a12..816df6a12 100644 --- a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-utils.test.js +++ b/ui/app/components/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-utils.test.js diff --git a/ui/app/components/send_/send-content/send-amount-row/index.js b/ui/app/components/send/send-content/send-amount-row/index.js index abc6852fe..abc6852fe 100644 --- a/ui/app/components/send_/send-content/send-amount-row/index.js +++ b/ui/app/components/send/send-content/send-amount-row/index.js diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js b/ui/app/components/send/send-content/send-amount-row/send-amount-row.component.js index 6e30d29a4..c548a5695 100644 --- a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js +++ b/ui/app/components/send/send-content/send-amount-row/send-amount-row.component.js @@ -2,7 +2,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import SendRowWrapper from '../send-row-wrapper/' import AmountMaxButton from './amount-max-button/' -import CurrencyDisplay from '../../../send/currency-display' +import CurrencyDisplay from '../../currency-display' export default class SendAmountRow extends Component { diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js b/ui/app/components/send/send-content/send-amount-row/send-amount-row.container.js index 3504d1b73..3504d1b73 100644 --- a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js +++ b/ui/app/components/send/send-content/send-amount-row/send-amount-row.container.js diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.scss b/ui/app/components/send/send-content/send-amount-row/send-amount-row.scss index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.scss +++ b/ui/app/components/send/send-content/send-amount-row/send-amount-row.scss diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.selectors.js b/ui/app/components/send/send-content/send-amount-row/send-amount-row.selectors.js index fb08c7ed7..fb08c7ed7 100644 --- a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.selectors.js +++ b/ui/app/components/send/send-content/send-amount-row/send-amount-row.selectors.js diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js b/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-component.test.js index 95c000a34..8425e076e 100644 --- a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js +++ b/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-component.test.js @@ -6,7 +6,7 @@ import SendAmountRow from '../send-amount-row.component.js' import SendRowWrapper from '../../send-row-wrapper/send-row-wrapper.component' import AmountMaxButton from '../amount-max-button/amount-max-button.container' -import CurrencyDisplay from '../../../../send/currency-display' +import CurrencyDisplay from '../../../currency-display' const propsMethodSpies = { setMaxModeTo: sinon.spy(), diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js b/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-container.test.js index 52e351aee..52e351aee 100644 --- a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js +++ b/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-container.test.js diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-selectors.test.js b/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-selectors.test.js index 4672cb8a7..4672cb8a7 100644 --- a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-selectors.test.js +++ b/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-selectors.test.js diff --git a/ui/app/components/send_/send-content/send-content-README.md b/ui/app/components/send/send-content/send-content-README.md index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-content-README.md +++ b/ui/app/components/send/send-content/send-content-README.md diff --git a/ui/app/components/send_/send-content/send-content.component.js b/ui/app/components/send/send-content/send-content.component.js index adc114c0e..7a0b1a18e 100644 --- a/ui/app/components/send_/send-content/send-content.component.js +++ b/ui/app/components/send/send-content/send-content.component.js @@ -4,6 +4,7 @@ import PageContainerContent from '../../page-container/page-container-content.co import SendAmountRow from './send-amount-row/' import SendFromRow from './send-from-row/' import SendGasRow from './send-gas-row/' +import SendHexDataRow from './send-hex-data-row' import SendToRow from './send-to-row/' export default class SendContent extends Component { @@ -20,6 +21,7 @@ export default class SendContent extends Component { <SendToRow updateGas={(updateData) => this.props.updateGas(updateData)} /> <SendAmountRow updateGas={(updateData) => this.props.updateGas(updateData)} /> <SendGasRow /> + <SendHexDataRow /> </div> </PageContainerContent> ) diff --git a/ui/app/components/send_/send-content/send-content.scss b/ui/app/components/send/send-content/send-content.scss index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-content.scss +++ b/ui/app/components/send/send-content/send-content.scss diff --git a/ui/app/components/send_/send-content/send-dropdown-list/index.js b/ui/app/components/send/send-content/send-dropdown-list/index.js index 04af6536c..04af6536c 100644 --- a/ui/app/components/send_/send-content/send-dropdown-list/index.js +++ b/ui/app/components/send/send-content/send-dropdown-list/index.js diff --git a/ui/app/components/send_/send-content/send-dropdown-list/send-dropdown-list.component.js b/ui/app/components/send/send-content/send-dropdown-list/send-dropdown-list.component.js index bedac1259..bedac1259 100644 --- a/ui/app/components/send_/send-content/send-dropdown-list/send-dropdown-list.component.js +++ b/ui/app/components/send/send-content/send-dropdown-list/send-dropdown-list.component.js diff --git a/ui/app/components/send_/send-content/send-dropdown-list/tests/send-dropdown-list-component.test.js b/ui/app/components/send/send-content/send-dropdown-list/tests/send-dropdown-list-component.test.js index b92dd4dfe..b92dd4dfe 100644 --- a/ui/app/components/send_/send-content/send-dropdown-list/tests/send-dropdown-list-component.test.js +++ b/ui/app/components/send/send-content/send-dropdown-list/tests/send-dropdown-list-component.test.js diff --git a/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown-README.md b/ui/app/components/send/send-content/send-from-row/from-dropdown/from-dropdown-README.md index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown-README.md +++ b/ui/app/components/send/send-content/send-from-row/from-dropdown/from-dropdown-README.md diff --git a/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown.component.js b/ui/app/components/send/send-content/send-from-row/from-dropdown/from-dropdown.component.js index 4f43a9d61..4f43a9d61 100644 --- a/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown.component.js +++ b/ui/app/components/send/send-content/send-from-row/from-dropdown/from-dropdown.component.js diff --git a/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown.scss b/ui/app/components/send/send-content/send-from-row/from-dropdown/from-dropdown.scss index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown.scss +++ b/ui/app/components/send/send-content/send-from-row/from-dropdown/from-dropdown.scss diff --git a/ui/app/components/send_/send-content/send-from-row/from-dropdown/index.js b/ui/app/components/send/send-content/send-from-row/from-dropdown/index.js index 2314ef4e3..2314ef4e3 100644 --- a/ui/app/components/send_/send-content/send-from-row/from-dropdown/index.js +++ b/ui/app/components/send/send-content/send-from-row/from-dropdown/index.js diff --git a/ui/app/components/send_/send-content/send-from-row/from-dropdown/tests/from-dropdown-component.test.js b/ui/app/components/send/send-content/send-from-row/from-dropdown/tests/from-dropdown-component.test.js index 84fcb281e..84fcb281e 100644 --- a/ui/app/components/send_/send-content/send-from-row/from-dropdown/tests/from-dropdown-component.test.js +++ b/ui/app/components/send/send-content/send-from-row/from-dropdown/tests/from-dropdown-component.test.js diff --git a/ui/app/components/send_/send-content/send-from-row/index.js b/ui/app/components/send/send-content/send-from-row/index.js index 0a79726b2..0a79726b2 100644 --- a/ui/app/components/send_/send-content/send-from-row/index.js +++ b/ui/app/components/send/send-content/send-from-row/index.js diff --git a/ui/app/components/send_/send-content/send-from-row/send-from-row-README.md b/ui/app/components/send/send-content/send-from-row/send-from-row-README.md index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-from-row/send-from-row-README.md +++ b/ui/app/components/send/send-content/send-from-row/send-from-row-README.md diff --git a/ui/app/components/send_/send-content/send-from-row/send-from-row.component.js b/ui/app/components/send/send-content/send-from-row/send-from-row.component.js index 3e0e0de22..3e0e0de22 100644 --- a/ui/app/components/send_/send-content/send-from-row/send-from-row.component.js +++ b/ui/app/components/send/send-content/send-from-row/send-from-row.component.js diff --git a/ui/app/components/send_/send-content/send-from-row/send-from-row.container.js b/ui/app/components/send/send-content/send-from-row/send-from-row.container.js index 33cb63b43..33cb63b43 100644 --- a/ui/app/components/send_/send-content/send-from-row/send-from-row.container.js +++ b/ui/app/components/send/send-content/send-from-row/send-from-row.container.js diff --git a/ui/app/components/send_/send-content/send-from-row/send-from-row.selectors.js b/ui/app/components/send/send-content/send-from-row/send-from-row.selectors.js index 03ef4806b..03ef4806b 100644 --- a/ui/app/components/send_/send-content/send-from-row/send-from-row.selectors.js +++ b/ui/app/components/send/send-content/send-from-row/send-from-row.selectors.js diff --git a/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-component.test.js b/ui/app/components/send/send-content/send-from-row/tests/send-from-row-component.test.js index 9ba8d1739..9ba8d1739 100644 --- a/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-component.test.js +++ b/ui/app/components/send/send-content/send-from-row/tests/send-from-row-component.test.js diff --git a/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-container.test.js b/ui/app/components/send/send-content/send-from-row/tests/send-from-row-container.test.js index e080b2fe3..e080b2fe3 100644 --- a/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-container.test.js +++ b/ui/app/components/send/send-content/send-from-row/tests/send-from-row-container.test.js diff --git a/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-selectors.test.js b/ui/app/components/send/send-content/send-from-row/tests/send-from-row-selectors.test.js index ecb57bbc3..ecb57bbc3 100644 --- a/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-selectors.test.js +++ b/ui/app/components/send/send-content/send-from-row/tests/send-from-row-selectors.test.js diff --git a/ui/app/components/send_/send-content/send-gas-row/README.md b/ui/app/components/send/send-content/send-gas-row/README.md index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-gas-row/README.md +++ b/ui/app/components/send/send-content/send-gas-row/README.md diff --git a/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js b/ui/app/components/send/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js index bb9a94428..bb9a94428 100644 --- a/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js +++ b/ui/app/components/send/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js diff --git a/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/index.js b/ui/app/components/send/send-content/send-gas-row/gas-fee-display/index.js index dba0edb7b..dba0edb7b 100644 --- a/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/index.js +++ b/ui/app/components/send/send-content/send-gas-row/gas-fee-display/index.js diff --git a/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js b/ui/app/components/send/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js index 7cbe8d0df..7cbe8d0df 100644 --- a/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js +++ b/ui/app/components/send/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js diff --git a/ui/app/components/send_/send-content/send-gas-row/index.js b/ui/app/components/send/send-content/send-gas-row/index.js index 3c7ff1d5f..3c7ff1d5f 100644 --- a/ui/app/components/send_/send-content/send-gas-row/index.js +++ b/ui/app/components/send/send-content/send-gas-row/index.js diff --git a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js b/ui/app/components/send/send-content/send-gas-row/send-gas-row.component.js index 91b58cfd0..91b58cfd0 100644 --- a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js +++ b/ui/app/components/send/send-content/send-gas-row/send-gas-row.component.js diff --git a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js b/ui/app/components/send/send-content/send-gas-row/send-gas-row.container.js index 8f8e3e4dd..8f8e3e4dd 100644 --- a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js +++ b/ui/app/components/send/send-content/send-gas-row/send-gas-row.container.js diff --git a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.scss b/ui/app/components/send/send-content/send-gas-row/send-gas-row.scss index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.scss +++ b/ui/app/components/send/send-content/send-gas-row/send-gas-row.scss diff --git a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.selectors.js b/ui/app/components/send/send-content/send-gas-row/send-gas-row.selectors.js index 96f6293c2..96f6293c2 100644 --- a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.selectors.js +++ b/ui/app/components/send/send-content/send-gas-row/send-gas-row.selectors.js diff --git a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js b/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-component.test.js index 54a92bd2d..54a92bd2d 100644 --- a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js +++ b/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-component.test.js diff --git a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js b/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-container.test.js index 2ce062505..2ce062505 100644 --- a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js +++ b/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-container.test.js diff --git a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-selectors.test.js b/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-selectors.test.js index d46dd9d8b..d46dd9d8b 100644 --- a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-selectors.test.js +++ b/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-selectors.test.js diff --git a/ui/app/components/send/send-content/send-hex-data-row/index.js b/ui/app/components/send/send-content/send-hex-data-row/index.js new file mode 100644 index 000000000..08c341067 --- /dev/null +++ b/ui/app/components/send/send-content/send-hex-data-row/index.js @@ -0,0 +1 @@ +export { default } from './send-hex-data-row.container' diff --git a/ui/app/components/send/send-content/send-hex-data-row/send-hex-data-row.component.js b/ui/app/components/send/send-content/send-hex-data-row/send-hex-data-row.component.js new file mode 100644 index 000000000..063930db3 --- /dev/null +++ b/ui/app/components/send/send-content/send-hex-data-row/send-hex-data-row.component.js @@ -0,0 +1,40 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import SendRowWrapper from '../send-row-wrapper' + +export default class SendHexDataRow extends Component { + static propTypes = { + data: PropTypes.string, + inError: PropTypes.bool, + updateSendHexData: PropTypes.func.isRequired, + }; + + static contextTypes = { + t: PropTypes.func, + }; + + onInput = (event) => { + const {updateSendHexData} = this.props + event.target.value = event.target.value.replace(/\n/g, '') + updateSendHexData(event.target.value || null) + } + + render () { + const {inError} = this.props + const {t} = this.context + + return ( + <SendRowWrapper + label={`${t('hexData')}:`} + showError={inError} + errorType={'amount'} + > + <textarea + onInput={this.onInput} + placeholder="Optional" + className="send-v2__hex-data__input" + /> + </SendRowWrapper> + ) + } +} diff --git a/ui/app/components/send/send-content/send-hex-data-row/send-hex-data-row.container.js b/ui/app/components/send/send-content/send-hex-data-row/send-hex-data-row.container.js new file mode 100644 index 000000000..df554ca5f --- /dev/null +++ b/ui/app/components/send/send-content/send-hex-data-row/send-hex-data-row.container.js @@ -0,0 +1,21 @@ +import { connect } from 'react-redux' +import { + updateSendHexData, +} from '../../../../actions' +import SendHexDataRow from './send-hex-data-row.component' + +export default connect(mapStateToProps, mapDispatchToProps)(SendHexDataRow) + +function mapStateToProps (state) { + return { + data: state.metamask.send.data, + } +} + +function mapDispatchToProps (dispatch) { + return { + updateSendHexData (data) { + return dispatch(updateSendHexData(data)) + }, + } +} diff --git a/ui/app/components/send_/send-content/send-row-wrapper/index.js b/ui/app/components/send/send-content/send-row-wrapper/index.js index d17545dcc..d17545dcc 100644 --- a/ui/app/components/send_/send-content/send-row-wrapper/index.js +++ b/ui/app/components/send/send-content/send-row-wrapper/index.js diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/index.js b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/index.js index c00617f83..c00617f83 100644 --- a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/index.js +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/index.js diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message-README.md b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message-README.md index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message-README.md +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message-README.md diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js index 61bc7bab7..61bc7bab7 100644 --- a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.container.js b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.container.js index 59622047f..59622047f 100644 --- a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.container.js +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.container.js diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.scss b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.scss index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.scss +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.scss diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/tests/send-row-error-message-component.test.js b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/tests/send-row-error-message-component.test.js index 2304a43d2..2304a43d2 100644 --- a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/tests/send-row-error-message-component.test.js +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/tests/send-row-error-message-component.test.js diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/tests/send-row-error-message-container.test.js b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/tests/send-row-error-message-container.test.js index eecff165d..eecff165d 100644 --- a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/tests/send-row-error-message-container.test.js +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-error-message/tests/send-row-error-message-container.test.js diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper-README.md b/ui/app/components/send/send-content/send-row-wrapper/send-row-wrapper-README.md index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper-README.md +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-wrapper-README.md diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper.component.js b/ui/app/components/send/send-content/send-row-wrapper/send-row-wrapper.component.js index b7528a15f..b7528a15f 100644 --- a/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper.component.js +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-wrapper.component.js diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper.scss b/ui/app/components/send/send-content/send-row-wrapper/send-row-wrapper.scss index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper.scss +++ b/ui/app/components/send/send-content/send-row-wrapper/send-row-wrapper.scss diff --git a/ui/app/components/send_/send-content/send-row-wrapper/tests/send-row-wrapper-component.test.js b/ui/app/components/send/send-content/send-row-wrapper/tests/send-row-wrapper-component.test.js index 30280e1d0..30280e1d0 100644 --- a/ui/app/components/send_/send-content/send-row-wrapper/tests/send-row-wrapper-component.test.js +++ b/ui/app/components/send/send-content/send-row-wrapper/tests/send-row-wrapper-component.test.js diff --git a/ui/app/components/send_/send-content/send-to-row/index.js b/ui/app/components/send/send-content/send-to-row/index.js index 121f15148..121f15148 100644 --- a/ui/app/components/send_/send-content/send-to-row/index.js +++ b/ui/app/components/send/send-content/send-to-row/index.js diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row-README.md b/ui/app/components/send/send-content/send-to-row/send-to-row-README.md index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-content/send-to-row/send-to-row-README.md +++ b/ui/app/components/send/send-content/send-to-row/send-to-row-README.md diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row.component.js b/ui/app/components/send/send-content/send-to-row/send-to-row.component.js index 892ad5d67..892ad5d67 100644 --- a/ui/app/components/send_/send-content/send-to-row/send-to-row.component.js +++ b/ui/app/components/send/send-content/send-to-row/send-to-row.component.js diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row.container.js b/ui/app/components/send/send-content/send-to-row/send-to-row.container.js index 1c9c9d518..1c9c9d518 100644 --- a/ui/app/components/send_/send-content/send-to-row/send-to-row.container.js +++ b/ui/app/components/send/send-content/send-to-row/send-to-row.container.js diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row.selectors.js b/ui/app/components/send/send-content/send-to-row/send-to-row.selectors.js index 8919014be..8919014be 100644 --- a/ui/app/components/send_/send-content/send-to-row/send-to-row.selectors.js +++ b/ui/app/components/send/send-content/send-to-row/send-to-row.selectors.js diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js b/ui/app/components/send/send-content/send-to-row/send-to-row.utils.js index 6b90a9f09..6b90a9f09 100644 --- a/ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js +++ b/ui/app/components/send/send-content/send-to-row/send-to-row.utils.js diff --git a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-component.test.js index 781371004..781371004 100644 --- a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js +++ b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-component.test.js diff --git a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-container.test.js b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-container.test.js index 92355c00a..92355c00a 100644 --- a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-container.test.js +++ b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-container.test.js diff --git a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-selectors.test.js b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-selectors.test.js index 122ad3265..122ad3265 100644 --- a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-selectors.test.js +++ b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-selectors.test.js diff --git a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-utils.test.js index 4d2447c32..4d2447c32 100644 --- a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js +++ b/ui/app/components/send/send-content/send-to-row/tests/send-to-row-utils.test.js diff --git a/ui/app/components/send_/send-content/tests/send-content-component.test.js b/ui/app/components/send/send-content/tests/send-content-component.test.js index d5bb6693c..d5bb6693c 100644 --- a/ui/app/components/send_/send-content/tests/send-content-component.test.js +++ b/ui/app/components/send/send-content/tests/send-content-component.test.js diff --git a/ui/app/components/send_/send-footer/README.md b/ui/app/components/send/send-footer/README.md index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-footer/README.md +++ b/ui/app/components/send/send-footer/README.md diff --git a/ui/app/components/send_/send-footer/index.js b/ui/app/components/send/send-footer/index.js index 58e91d622..58e91d622 100644 --- a/ui/app/components/send_/send-footer/index.js +++ b/ui/app/components/send/send-footer/index.js diff --git a/ui/app/components/send_/send-footer/send-footer.component.js b/ui/app/components/send/send-footer/send-footer.component.js index 2085f1dce..518cff06e 100644 --- a/ui/app/components/send_/send-footer/send-footer.component.js +++ b/ui/app/components/send/send-footer/send-footer.component.js @@ -8,6 +8,7 @@ export default class SendFooter extends Component { static propTypes = { addToAddressBookIfNew: PropTypes.func, amount: PropTypes.string, + data: PropTypes.string, clearSend: PropTypes.func, disabled: PropTypes.bool, editingTransactionId: PropTypes.string, @@ -41,6 +42,7 @@ export default class SendFooter extends Component { const { addToAddressBookIfNew, amount, + data, editingTransactionId, from: {address: from}, gasLimit: gas, @@ -68,6 +70,7 @@ export default class SendFooter extends Component { const promise = editingTransactionId ? update({ amount, + data, editingTransactionId, from, gas, @@ -76,7 +79,7 @@ export default class SendFooter extends Component { to, unapprovedTxs, }) - : sign({ selectedToken, to, amount, from, gas, gasPrice }) + : sign({ data, selectedToken, to, amount, from, gas, gasPrice }) Promise.resolve(promise) .then(() => history.push(CONFIRM_TRANSACTION_ROUTE)) diff --git a/ui/app/components/send_/send-footer/send-footer.container.js b/ui/app/components/send/send-footer/send-footer.container.js index 0af6fcfa1..60de4d030 100644 --- a/ui/app/components/send_/send-footer/send-footer.container.js +++ b/ui/app/components/send/send-footer/send-footer.container.js @@ -18,6 +18,7 @@ import { getSendFromObject, getSendTo, getSendToAccounts, + getSendHexData, getTokenBalance, getUnapprovedTxs, } from '../send.selectors' @@ -35,6 +36,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(SendFooter) function mapStateToProps (state) { return { amount: getSendAmount(state), + data: getSendHexData(state), editingTransactionId: getSendEditingTransactionId(state), from: getSendFromObject(state), gasLimit: getGasLimit(state), @@ -52,9 +54,10 @@ function mapStateToProps (state) { function mapDispatchToProps (dispatch) { return { clearSend: () => dispatch(clearSend()), - sign: ({ selectedToken, to, amount, from, gas, gasPrice }) => { + sign: ({ selectedToken, to, amount, from, gas, gasPrice, data }) => { const txParams = constructTxParams({ amount, + data, from, gas, gasPrice, @@ -68,6 +71,7 @@ function mapDispatchToProps (dispatch) { }, update: ({ amount, + data, editingTransactionId, from, gas, @@ -78,6 +82,7 @@ function mapDispatchToProps (dispatch) { }) => { const editingTx = constructUpdatedTx({ amount, + data, editingTransactionId, from, gas, diff --git a/ui/app/components/send_/send-footer/send-footer.scss b/ui/app/components/send/send-footer/send-footer.scss index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-footer/send-footer.scss +++ b/ui/app/components/send/send-footer/send-footer.scss diff --git a/ui/app/components/send_/send-footer/send-footer.selectors.js b/ui/app/components/send/send-footer/send-footer.selectors.js index e20addfdc..e20addfdc 100644 --- a/ui/app/components/send_/send-footer/send-footer.selectors.js +++ b/ui/app/components/send/send-footer/send-footer.selectors.js diff --git a/ui/app/components/send_/send-footer/send-footer.utils.js b/ui/app/components/send/send-footer/send-footer.utils.js index 875e7d948..f82ff1e9b 100644 --- a/ui/app/components/send_/send-footer/send-footer.utils.js +++ b/ui/app/components/send/send-footer/send-footer.utils.js @@ -8,8 +8,9 @@ function addHexPrefixToObjectValues (obj) { }, {}) } -function constructTxParams ({ selectedToken, to, amount, from, gas, gasPrice }) { +function constructTxParams ({ selectedToken, data, to, amount, from, gas, gasPrice }) { const txParams = { + data, from, value: '0', gas, @@ -21,13 +22,12 @@ function constructTxParams ({ selectedToken, to, amount, from, gas, gasPrice }) txParams.to = to } - const hexPrefixedTxParams = addHexPrefixToObjectValues(txParams) - - return hexPrefixedTxParams + return addHexPrefixToObjectValues(txParams) } function constructUpdatedTx ({ amount, + data, editingTransactionId, from, gas, @@ -36,9 +36,21 @@ function constructUpdatedTx ({ to, unapprovedTxs, }) { + const unapprovedTx = unapprovedTxs[editingTransactionId] + const txParamsData = unapprovedTx.txParams.data ? unapprovedTx.txParams.data : data const editingTx = { - ...unapprovedTxs[editingTransactionId], - txParams: addHexPrefixToObjectValues({ from, gas, gasPrice }), + ...unapprovedTx, + txParams: Object.assign( + unapprovedTx.txParams, + addHexPrefixToObjectValues({ + data: txParamsData, + to, + from, + gas, + gasPrice, + value: amount, + }) + ), } if (selectedToken) { @@ -52,18 +64,10 @@ function constructUpdatedTx ({ to: selectedToken.address, data, })) - } else { - const { data } = unapprovedTxs[editingTransactionId].txParams - - Object.assign(editingTx.txParams, addHexPrefixToObjectValues({ - value: amount, - to, - data, - })) + } - if (typeof editingTx.txParams.data === 'undefined') { - delete editingTx.txParams.data - } + if (typeof editingTx.txParams.data === 'undefined') { + delete editingTx.txParams.data } return editingTx diff --git a/ui/app/components/send_/send-footer/tests/send-footer-component.test.js b/ui/app/components/send/send-footer/tests/send-footer-component.test.js index 4b2cd327d..65e4bb654 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-component.test.js +++ b/ui/app/components/send/send-footer/tests/send-footer-component.test.js @@ -129,6 +129,7 @@ describe('SendFooter Component', function () { assert.deepEqual( propsMethodSpies.update.getCall(0).args[0], { + data: undefined, amount: 'mockAmount', editingTransactionId: 'mockEditingTransactionId', from: 'mockAddress', @@ -152,6 +153,7 @@ describe('SendFooter Component', function () { assert.deepEqual( propsMethodSpies.sign.getCall(0).args[0], { + data: undefined, amount: 'mockAmount', from: 'mockAddress', gas: 'mockGasLimit', diff --git a/ui/app/components/send_/send-footer/tests/send-footer-container.test.js b/ui/app/components/send/send-footer/tests/send-footer-container.test.js index 39d6a7686..cf4c893ee 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-container.test.js +++ b/ui/app/components/send/send-footer/tests/send-footer-container.test.js @@ -38,6 +38,7 @@ proxyquire('../send-footer.container.js', { getSendTo: (s) => `mockTo:${s}`, getSendToAccounts: (s) => `mockToAccounts:${s}`, getTokenBalance: (s) => `mockTokenBalance:${s}`, + getSendHexData: (s) => `mockHexData:${s}`, getUnapprovedTxs: (s) => `mockUnapprovedTxs:${s}`, }, './send-footer.selectors': { isSendFormInError: (s) => `mockInError:${s}` }, @@ -51,6 +52,7 @@ describe('send-footer container', () => { it('should map the correct properties to props', () => { assert.deepEqual(mapStateToProps('mockState'), { amount: 'mockAmount:mockState', + data: 'mockHexData:mockState', selectedToken: 'mockSelectedToken:mockState', editingTransactionId: 'mockEditingTransactionId:mockState', from: 'mockFromObject:mockState', @@ -100,6 +102,7 @@ describe('send-footer container', () => { assert.deepEqual( utilsStubs.constructTxParams.getCall(0).args[0], { + data: undefined, selectedToken: { address: '0xabc', }, @@ -129,6 +132,7 @@ describe('send-footer container', () => { assert.deepEqual( utilsStubs.constructTxParams.getCall(0).args[0], { + data: undefined, selectedToken: undefined, to: 'mockTo', amount: 'mockAmount', @@ -160,6 +164,7 @@ describe('send-footer container', () => { assert.deepEqual( utilsStubs.constructUpdatedTx.getCall(0).args[0], { + data: undefined, to: 'mockTo', amount: 'mockAmount', from: 'mockFrom', diff --git a/ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js b/ui/app/components/send/send-footer/tests/send-footer-selectors.test.js index 8de032f57..8de032f57 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js +++ b/ui/app/components/send/send-footer/tests/send-footer-selectors.test.js diff --git a/ui/app/components/send_/send-footer/tests/send-footer-utils.test.js b/ui/app/components/send/send-footer/tests/send-footer-utils.test.js index 2d3135995..28ff0c891 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-utils.test.js +++ b/ui/app/components/send/send-footer/tests/send-footer-utils.test.js @@ -65,6 +65,28 @@ describe('send-footer utils', () => { }) describe('constructTxParams()', () => { + it('should return a new txParams object with data if there data is given', () => { + assert.deepEqual( + constructTxParams({ + data: 'someData', + selectedToken: false, + to: 'mockTo', + amount: 'mockAmount', + from: 'mockFrom', + gas: 'mockGas', + gasPrice: 'mockGasPrice', + }), + { + data: '0xsomeData', + to: '0xmockTo', + value: '0xmockAmount', + from: '0xmockFrom', + gas: '0xmockGas', + gasPrice: '0xmockGasPrice', + } + ) + }) + it('should return a new txParams object with value and to properties if there is no selectedToken', () => { assert.deepEqual( constructTxParams({ @@ -76,6 +98,7 @@ describe('send-footer utils', () => { gasPrice: 'mockGasPrice', }), { + data: undefined, to: '0xmockTo', value: '0xmockAmount', from: '0xmockFrom', @@ -96,6 +119,7 @@ describe('send-footer utils', () => { gasPrice: 'mockGasPrice', }), { + data: undefined, value: '0x0', from: '0xmockFrom', gas: '0xmockGas', diff --git a/ui/app/components/send_/send-header/README.md b/ui/app/components/send/send-header/README.md index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send-header/README.md +++ b/ui/app/components/send/send-header/README.md diff --git a/ui/app/components/send_/send-header/index.js b/ui/app/components/send/send-header/index.js index 0b17f0b7d..0b17f0b7d 100644 --- a/ui/app/components/send_/send-header/index.js +++ b/ui/app/components/send/send-header/index.js diff --git a/ui/app/components/send_/send-header/send-header.component.js b/ui/app/components/send/send-header/send-header.component.js index efc4bbf27..efc4bbf27 100644 --- a/ui/app/components/send_/send-header/send-header.component.js +++ b/ui/app/components/send/send-header/send-header.component.js diff --git a/ui/app/components/send_/send-header/send-header.container.js b/ui/app/components/send/send-header/send-header.container.js index 4bcd0d1b6..4bcd0d1b6 100644 --- a/ui/app/components/send_/send-header/send-header.container.js +++ b/ui/app/components/send/send-header/send-header.container.js diff --git a/ui/app/components/send_/send-header/send-header.selectors.js b/ui/app/components/send/send-header/send-header.selectors.js index d7c9d3766..d7c9d3766 100644 --- a/ui/app/components/send_/send-header/send-header.selectors.js +++ b/ui/app/components/send/send-header/send-header.selectors.js diff --git a/ui/app/components/send_/send-header/tests/send-header-component.test.js b/ui/app/components/send/send-header/tests/send-header-component.test.js index 930bfa387..930bfa387 100644 --- a/ui/app/components/send_/send-header/tests/send-header-component.test.js +++ b/ui/app/components/send/send-header/tests/send-header-component.test.js diff --git a/ui/app/components/send_/send-header/tests/send-header-container.test.js b/ui/app/components/send/send-header/tests/send-header-container.test.js index 41a7e8a89..41a7e8a89 100644 --- a/ui/app/components/send_/send-header/tests/send-header-container.test.js +++ b/ui/app/components/send/send-header/tests/send-header-container.test.js diff --git a/ui/app/components/send_/send-header/tests/send-header-selectors.test.js b/ui/app/components/send/send-header/tests/send-header-selectors.test.js index e0c6a3ab3..e0c6a3ab3 100644 --- a/ui/app/components/send_/send-header/tests/send-header-selectors.test.js +++ b/ui/app/components/send/send-header/tests/send-header-selectors.test.js diff --git a/ui/app/components/send_/send.component.js b/ui/app/components/send/send.component.js index 6f1b20c55..6f1b20c55 100644 --- a/ui/app/components/send_/send.component.js +++ b/ui/app/components/send/send.component.js diff --git a/ui/app/components/send_/send.constants.js b/ui/app/components/send/send.constants.js index 8acdf0641..8acdf0641 100644 --- a/ui/app/components/send_/send.constants.js +++ b/ui/app/components/send/send.constants.js diff --git a/ui/app/components/send_/send.container.js b/ui/app/components/send/send.container.js index 44ebd2792..44ebd2792 100644 --- a/ui/app/components/send_/send.container.js +++ b/ui/app/components/send/send.container.js diff --git a/ui/app/components/send_/send.scss b/ui/app/components/send/send.scss index e69de29bb..e69de29bb 100644 --- a/ui/app/components/send_/send.scss +++ b/ui/app/components/send/send.scss diff --git a/ui/app/components/send_/send.selectors.js b/ui/app/components/send/send.selectors.js index f910f7caf..cf07eafe1 100644 --- a/ui/app/components/send_/send.selectors.js +++ b/ui/app/components/send/send.selectors.js @@ -33,6 +33,7 @@ const selectors = { getSelectedTokenExchangeRate, getSelectedTokenToFiatRate, getSendAmount, + getSendHexData, getSendEditingTransactionId, getSendErrors, getSendFrom, @@ -210,6 +211,10 @@ function getSendAmount (state) { return state.metamask.send.amount } +function getSendHexData (state) { + return state.metamask.send.data +} + function getSendEditingTransactionId (state) { return state.metamask.send.editingTransactionId } diff --git a/ui/app/components/send_/send.utils.js b/ui/app/components/send/send.utils.js index aa255c3d4..aa255c3d4 100644 --- a/ui/app/components/send_/send.utils.js +++ b/ui/app/components/send/send.utils.js diff --git a/ui/app/components/send_/tests/send-component.test.js b/ui/app/components/send/tests/send-component.test.js index 6194ec508..6194ec508 100644 --- a/ui/app/components/send_/tests/send-component.test.js +++ b/ui/app/components/send/tests/send-component.test.js diff --git a/ui/app/components/send_/tests/send-container.test.js b/ui/app/components/send/tests/send-container.test.js index 7a9120d24..7a9120d24 100644 --- a/ui/app/components/send_/tests/send-container.test.js +++ b/ui/app/components/send/tests/send-container.test.js diff --git a/ui/app/components/send_/tests/send-selectors-test-data.js b/ui/app/components/send/tests/send-selectors-test-data.js index 8f9c19314..8f9c19314 100644 --- a/ui/app/components/send_/tests/send-selectors-test-data.js +++ b/ui/app/components/send/tests/send-selectors-test-data.js diff --git a/ui/app/components/send_/tests/send-selectors.test.js b/ui/app/components/send/tests/send-selectors.test.js index 218da656b..218da656b 100644 --- a/ui/app/components/send_/tests/send-selectors.test.js +++ b/ui/app/components/send/tests/send-selectors.test.js diff --git a/ui/app/components/send_/tests/send-utils.test.js b/ui/app/components/send/tests/send-utils.test.js index b8579e0e4..18dde495a 100644 --- a/ui/app/components/send_/tests/send-utils.test.js +++ b/ui/app/components/send/tests/send-utils.test.js @@ -58,6 +58,7 @@ const { calcTokenBalance, isBalanceSufficient, isTokenBalanceSufficient, + removeLeadingZeroes, } = sendUtils describe('send utils', () => { @@ -483,4 +484,29 @@ describe('send utils', () => { assert.equal(getToAddressForGasUpdate(undefined, 'B'), 'b') }) }) + + describe('removeLeadingZeroes()', () => { + it('should remove leading zeroes from int when user types', () => { + assert.equal(removeLeadingZeroes('0'), '0') + assert.equal(removeLeadingZeroes('1'), '1') + assert.equal(removeLeadingZeroes('00'), '0') + assert.equal(removeLeadingZeroes('01'), '1') + }) + + it('should remove leading zeroes from int when user copy/paste', () => { + assert.equal(removeLeadingZeroes('001'), '1') + }) + + it('should remove leading zeroes from float when user types', () => { + assert.equal(removeLeadingZeroes('0.'), '0.') + assert.equal(removeLeadingZeroes('0.0'), '0.0') + assert.equal(removeLeadingZeroes('0.00'), '0.00') + assert.equal(removeLeadingZeroes('0.001'), '0.001') + assert.equal(removeLeadingZeroes('0.10'), '0.10') + }) + + it('should remove leading zeroes from float when user copy/paste', () => { + assert.equal(removeLeadingZeroes('00.1'), '0.1') + }) + }) }) diff --git a/ui/app/components/send/to-autocomplete.component.js b/ui/app/components/send/to-autocomplete.component.js index 19f534b94..9e270db75 100644 --- a/ui/app/components/send/to-autocomplete.component.js +++ b/ui/app/components/send/to-autocomplete.component.js @@ -1,7 +1,7 @@ import React, {Component} from 'react' import PropTypes from 'prop-types' import classnames from 'classnames' -import AccountListItem from '../send_/account-list-item/account-list-item.component' +import AccountListItem from '../send/account-list-item/account-list-item.component' export default class ToAutoComplete extends Component { diff --git a/ui/app/components/send/to-autocomplete/index.js b/ui/app/components/send/to-autocomplete/index.js new file mode 100644 index 000000000..244d301d1 --- /dev/null +++ b/ui/app/components/send/to-autocomplete/index.js @@ -0,0 +1 @@ +export { default } from './to-autocomplete.js' diff --git a/ui/app/components/send/to-autocomplete/to-autocomplete.js b/ui/app/components/send/to-autocomplete/to-autocomplete.js new file mode 100644 index 000000000..80cfa7a85 --- /dev/null +++ b/ui/app/components/send/to-autocomplete/to-autocomplete.js @@ -0,0 +1,120 @@ +const Component = require('react').Component +const PropTypes = require('prop-types') +const h = require('react-hyperscript') +const inherits = require('util').inherits +const AccountListItem = require('../account-list-item/account-list-item.component').default +const connect = require('react-redux').connect + +ToAutoComplete.contextTypes = { + t: PropTypes.func, +} + +module.exports = connect()(ToAutoComplete) + + +inherits(ToAutoComplete, Component) +function ToAutoComplete () { + Component.call(this) + + this.state = { accountsToRender: [] } +} + +ToAutoComplete.prototype.getListItemIcon = function (listItemAddress, toAddress) { + const listItemIcon = h(`i.fa.fa-check.fa-lg`, { style: { color: '#02c9b1' } }) + + return toAddress && listItemAddress === toAddress + ? listItemIcon + : null +} + +ToAutoComplete.prototype.renderDropdown = function () { + const { + closeDropdown, + onChange, + to, + } = this.props + const { accountsToRender } = this.state + + return accountsToRender.length && h('div', {}, [ + + h('div.send-v2__from-dropdown__close-area', { + onClick: closeDropdown, + }), + + h('div.send-v2__from-dropdown__list', {}, [ + + ...accountsToRender.map(account => h(AccountListItem, { + account, + className: 'account-list-item__dropdown', + handleClick: () => { + onChange(account.address) + closeDropdown() + }, + icon: this.getListItemIcon(account.address, to), + displayBalance: false, + displayAddress: true, + })), + + ]), + + ]) +} + +ToAutoComplete.prototype.handleInputEvent = function (event = {}, cb) { + const { + to, + accounts, + closeDropdown, + openDropdown, + } = this.props + + const matchingAccounts = accounts.filter(({ address }) => address.match(to || '')) + const matches = matchingAccounts.length + + if (!matches || matchingAccounts[0].address === to) { + this.setState({ accountsToRender: [] }) + event.target && event.target.select() + closeDropdown() + } else { + this.setState({ accountsToRender: matchingAccounts }) + openDropdown() + } + cb && cb(event.target.value) +} + +ToAutoComplete.prototype.componentDidUpdate = function (nextProps, nextState) { + if (this.props.to !== nextProps.to) { + this.handleInputEvent() + } +} + +ToAutoComplete.prototype.render = function () { + const { + to, + dropdownOpen, + onChange, + inError, + } = this.props + + return h('div.send-v2__to-autocomplete', {}, [ + + h('input.send-v2__to-autocomplete__input', { + placeholder: this.context.t('recipientAddress'), + className: inError ? `send-v2__error-border` : '', + value: to, + onChange: event => onChange(event.target.value), + onFocus: event => this.handleInputEvent(event), + style: { + borderColor: inError ? 'red' : null, + }, + }), + + !to && h(`i.fa.fa-caret-down.fa-lg.send-v2__to-autocomplete__down-caret`, { + style: { color: '#dedede' }, + onClick: () => this.handleInputEvent(), + }), + + dropdownOpen && this.renderDropdown(), + + ]) +} diff --git a/ui/app/components/send_/send.utils.test.js b/ui/app/components/send_/send.utils.test.js deleted file mode 100644 index 36f3a5c10..000000000 --- a/ui/app/components/send_/send.utils.test.js +++ /dev/null @@ -1,30 +0,0 @@ -import assert from 'assert' -import { removeLeadingZeroes } from './send.utils' - - -describe('send utils', () => { - describe('removeLeadingZeroes()', () => { - it('should remove leading zeroes from int when user types', () => { - assert.equal(removeLeadingZeroes('0'), '0') - assert.equal(removeLeadingZeroes('1'), '1') - assert.equal(removeLeadingZeroes('00'), '0') - assert.equal(removeLeadingZeroes('01'), '1') - }) - - it('should remove leading zeroes from int when user copy/paste', () => { - assert.equal(removeLeadingZeroes('001'), '1') - }) - - it('should remove leading zeroes from float when user types', () => { - assert.equal(removeLeadingZeroes('0.'), '0.') - assert.equal(removeLeadingZeroes('0.0'), '0.0') - assert.equal(removeLeadingZeroes('0.00'), '0.00') - assert.equal(removeLeadingZeroes('0.001'), '0.001') - assert.equal(removeLeadingZeroes('0.10'), '0.10') - }) - - it('should remove leading zeroes from float when user copy/paste', () => { - assert.equal(removeLeadingZeroes('00.1'), '0.1') - }) - }) -}) diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js index e539514ec..0d693b805 100644 --- a/ui/app/components/tx-list-item.js +++ b/ui/app/components/tx-list-item.js @@ -307,20 +307,16 @@ TxListItem.prototype.render = function () { ]), ]), - this.showRetryButton() && h('div.tx-list-item-retry-container', [ - - h('span.tx-list-item-retry-copy', 'Taking too long?'), - - h('span.tx-list-item-retry-link', { - onClick: (event) => { - event.stopPropagation() - if (isTokenTx) { - this.setSelectedToken(txParams.to) - } - this.resubmit() - }, - }, 'Increase the gas price on your transaction'), - + this.showRetryButton() && h('.tx-list-item-retry-container', { + onClick: (event) => { + event.stopPropagation() + if (isTokenTx) { + this.setSelectedToken(txParams.to) + } + this.resubmit() + }, + }, [ + h('span', 'Taking too long? Increase the gas price on your transaction'), ]), ]), // holding on icon from design diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js index da142fad8..20c2be0f1 100644 --- a/ui/app/components/wallet-view.js +++ b/ui/app/components/wallet-view.js @@ -175,7 +175,7 @@ WalletView.prototype.render = function () { this.setState({ copyToClipboardPressed: false }) }, }, [ - `${checksummedAddress.slice(0, 4)}...${checksummedAddress.slice(-4)}`, + `${checksummedAddress.slice(0, 6)}...${checksummedAddress.slice(-4)}`, h('i.fa.fa-clipboard', { style: { marginLeft: '8px' } }), ]), ]), diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index 4e8aaa07d..112ea6bca 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -9,11 +9,7 @@ const txHelper = require('../lib/tx-helper') const log = require('loglevel') const R = require('ramda') -const PendingTx = require('./components/pending-tx') const SignatureRequest = require('./components/signature-request') -// const PendingMsg = require('./components/pending-msg') -// const PendingPersonalMsg = require('./components/pending-personal-msg') -// const PendingTypedMsg = require('./components/pending-typed-msg') const Loading = require('./components/loading-screen') const { DEFAULT_ROUTE } = require('./routes') @@ -151,101 +147,32 @@ ConfirmTxScreen.prototype.render = function () { currentCurrency, conversionRate, blockGasLimit, - // provider, - // computedBalances, } = props var txData = this.getTxData() || {} - var txParams = txData.params || {} - - // var isNotification = isPopupOrNotification() === 'notification' - /* - Client is using the flag above to render the following in conf screen - // subtitle and nav - h('.section-title.flex-row.flex-center', [ - !isNotification ? h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', { - onClick: this.goHome.bind(this), - }) : null, - h('h2.page-subtitle', 'Confirm Transaction'), - isNotification ? h(NetworkIndicator, { - network: network, - provider: provider, - }) : null, - ]), - */ - - return currentTxView({ - // Properties - txData: txData, - key: txData.id, - selectedAddress: props.selectedAddress, - accounts: props.accounts, - identities: props.identities, - conversionRate, - currentCurrency, - blockGasLimit, - // Actions - buyEth: this.buyEth.bind(this, txParams.from || props.selectedAddress), - sendTransaction: this.sendTransaction.bind(this), - cancelTransaction: this.cancelTransaction.bind(this, txData), - signMessage: this.signMessage.bind(this, txData), - signPersonalMessage: this.signPersonalMessage.bind(this, txData), - signTypedMessage: this.signTypedMessage.bind(this, txData), - cancelMessage: this.cancelMessage.bind(this, txData), - cancelPersonalMessage: this.cancelPersonalMessage.bind(this, txData), - cancelTypedMessage: this.cancelTypedMessage.bind(this, txData), - }) -} - -function currentTxView (opts) { - log.info('rendering current tx view') - const { txData } = opts - const { txParams, msgParams } = txData - - if (txParams) { - log.debug('txParams detected, rendering pending tx') - return h(PendingTx, opts) - } else if (msgParams) { - log.debug('msgParams detected, rendering pending msg') - - return h(SignatureRequest, opts) - - // if (type === 'eth_sign') { - // log.debug('rendering eth_sign message') - // return h(PendingMsg, opts) - // } else if (type === 'personal_sign') { - // log.debug('rendering personal_sign message') - // return h(PendingPersonalMsg, opts) - // } else if (type === 'eth_signTypedData') { - // log.debug('rendering eth_signTypedData message') - // return h(PendingTypedMsg, opts) - // } - } - - return h(Loading) -} - -ConfirmTxScreen.prototype.buyEth = function (address, event) { - event.preventDefault() - this.props.dispatch(actions.buyEthView(address)) -} - -ConfirmTxScreen.prototype.sendTransaction = function (txData, event) { - this.stopPropagation(event) - this.props.dispatch(actions.updateAndApproveTx(txData)) - .then(() => this.props.history.push(DEFAULT_ROUTE)) -} - -ConfirmTxScreen.prototype.cancelTransaction = function (txData, event) { - this.stopPropagation(event) - event.preventDefault() - this.props.dispatch(actions.cancelTx(txData)) -} - -ConfirmTxScreen.prototype.cancelAllTransactions = function (unconfTxList, event) { - this.stopPropagation(event) - event.preventDefault() - this.props.dispatch(actions.cancelAllTx(unconfTxList)) + const { msgParams } = txData + log.debug('msgParams detected, rendering pending msg') + + return msgParams + ? h(SignatureRequest, { + // Properties + txData: txData, + key: txData.id, + selectedAddress: props.selectedAddress, + accounts: props.accounts, + identities: props.identities, + conversionRate, + currentCurrency, + blockGasLimit, + // Actions + signMessage: this.signMessage.bind(this, txData), + signPersonalMessage: this.signPersonalMessage.bind(this, txData), + signTypedMessage: this.signTypedMessage.bind(this, txData), + cancelMessage: this.cancelMessage.bind(this, txData), + cancelPersonalMessage: this.cancelPersonalMessage.bind(this, txData), + cancelTypedMessage: this.cancelTypedMessage.bind(this, txData), + }) + : h(Loading) } ConfirmTxScreen.prototype.signMessage = function (msgData, event) { @@ -295,20 +222,3 @@ ConfirmTxScreen.prototype.cancelTypedMessage = function (msgData, event) { this.stopPropagation(event) return this.props.dispatch(actions.cancelTypedMsg(msgData)) } - -ConfirmTxScreen.prototype.goHome = function (event) { - this.stopPropagation(event) - this.props.dispatch(actions.goHome()) -} - -// function warningIfExists (warning) { -// if (warning && -// // Do not display user rejections on this screen: -// warning.indexOf('User denied transaction signature') === -1) { -// return h('.error', { -// style: { -// margin: 'auto', -// }, -// }, warning) -// } -// } diff --git a/ui/app/css/itcss/components/account-menu.scss b/ui/app/css/itcss/components/account-menu.scss index 96fba890c..b14753e23 100644 --- a/ui/app/css/itcss/components/account-menu.scss +++ b/ui/app/css/itcss/components/account-menu.scss @@ -72,6 +72,7 @@ background-color: $dusty-gray; color: $black; font-weight: normal; + letter-spacing: .5px; } } @@ -84,6 +85,23 @@ @media screen and (max-width: 575px) { padding: 12px 14px; } + + .remove-account-icon { + width: 15px; + margin-left: 10px; + height: 15px; + } + + &:hover { + .remove-account-icon::after { + content: '\00D7'; + font-size: 25px; + color: $white; + cursor: pointer; + position: absolute; + margin-top: -5px; + } + } } &__account-info { diff --git a/ui/app/css/itcss/components/alert.scss b/ui/app/css/itcss/components/alert.scss new file mode 100644 index 000000000..930fc3f54 --- /dev/null +++ b/ui/app/css/itcss/components/alert.scss @@ -0,0 +1,57 @@ +.global-alert { + position: relative; + width: 100%; + background-color: #33A4E7; + + .msg { + width: 100%; + display: block; + color: white; + font-size: 12px; + text-align: center; + } +} + +.global-alert.hidden { + animation: alertHidden .5s ease forwards; +} + +.global-alert.visible { + animation: alert .5s ease forwards; +} + +/* Animation */ +@keyframes alert { + 0% { + opacity: 0; + top: -50px; + padding: 0px; + line-height: 12px; + } + + 50% { + opacity: 1; + } + + 100% { + top: 0; + padding: 8px; + line-height: 12px; + } +} + +@keyframes alertHidden { + 0% { + top: 0; + opacity: 1; + padding: 8px; + line-height: 12px; + } + + 100% { + opacity: 0; + top: -50px; + padding: 0px; + line-height: 0px; + } +} diff --git a/ui/app/css/itcss/components/index.scss b/ui/app/css/itcss/components/index.scss index 5be040906..96ad5fe64 100644 --- a/ui/app/css/itcss/components/index.scss +++ b/ui/app/css/itcss/components/index.scss @@ -8,6 +8,8 @@ @import './modal.scss'; +@import './alert.scss'; + @import './newui-sections.scss'; @import './account-dropdown.scss'; diff --git a/ui/app/css/itcss/components/new-account.scss b/ui/app/css/itcss/components/new-account.scss index 293579058..b12afb124 100644 --- a/ui/app/css/itcss/components/new-account.scss +++ b/ui/app/css/itcss/components/new-account.scss @@ -1,9 +1,8 @@ .new-account { - width: 376px; + width: 375px; background-color: #FFFFFF; box-shadow: 0 0 7px 0 rgba(0,0,0,0.08); z-index: 25; - padding-bottom: 31px; &__header { display: flex; @@ -28,7 +27,6 @@ &__tab { height: 54px; - width: 75px; padding: 15px 10px; color: $dusty-gray; font-family: Roboto; @@ -38,10 +36,6 @@ cursor: pointer; } - &__tab:first-of-type { - margin-right: 20px; - } - &__tab:hover { color: $black; border-bottom: none; @@ -69,7 +63,7 @@ display: flex; flex-flow: column; align-items: center; - padding: 0 30px; + padding: 0 30px 30px; &__select-section { display: flex; @@ -158,11 +152,296 @@ } } +.hw-tutorial { + width: 375px; + border-top: 1px solid #D2D8DD; + border-bottom: 1px solid #D2D8DD; + overflow: visible; + display: block; + padding: 15px 30px; +} + +.hw-connect { + &__header { + &__title { + margin-top: 5px; + margin-bottom: 15px; + font-size: 22px; + text-align: center; + } + + &__msg { + font-size: 14px; + color: #9b9b9b; + margin-top: 10px; + margin-bottom: 0px; + } + } + + &__learn-more { + margin-top: 15px; + font-size: 14px; + color: #5B5D67; + line-height: 19px; + text-align: center; + cursor: pointer; + + &__arrow { + transform: rotate(90deg); + display: block; + text-align: center; + height: 30px; + margin: 0px auto 10px; + } + } + + &__title { + padding-top: 10px; + font-weight: 400; + font-size: 18px; + } + + &__msg { + font-size: 14px; + color: #9b9b9b; + margin-top: 10px; + margin-bottom: 15px; + } + + &__link { + color: #2f9ae0; + } + + &__footer { + width: 100%; + + &__title { + padding-top: 15px; + padding-bottom: 12px; + font-weight: 400; + font-size: 18px; + text-align: center; + } + + &__msg { + font-size: 14px; + color: #9b9b9b; + margin-top: 12px; + margin-bottom: 27px; + } + + &__link { + color: #2f9ae0; + margin-left: 5px; + } + } + + &__get-trezor { + width: 100%; + padding-bottom: 20px; + padding-top: 20px; + + &__msg { + font-size: 14px; + color: #9b9b9b; + } + + &__link { + font-size: 14px; + text-align: center; + color: #2f9ae0; + cursor: pointer; + } + } + + &__step-asset { + margin: 0px auto 20px; + display: flex; + } +} + +.hw-account-list { + display: flex; + flex: 1; + flex-flow: column; + width: 100%; + + &__title_wrapper { + display: flex; + flex-direction: row; + flex: 1; + } + + &__title { + margin-bottom: 23px; + align-self: flex-start; + color: $scorpion; + font-family: Roboto; + font-size: 16px; + line-height: 21px; + font-weight: bold; + display: flex; + flex: 1; + } + + &__device { + margin-bottom: 23px; + align-self: flex-end; + color: $scorpion; + font-family: Roboto; + font-size: 16px; + line-height: 21px; + font-weight: normal; + display: flex; + } + + &__item { + font-size: 15px; + flex-direction: row; + display: flex; + padding-left: 10px; + padding-right: 10px; + } + + &__item:nth-of-type(even) { + background-color: #fbfbfb; + } + + &__item:nth-of-type(odd) { + background: rgba(0, 0, 0, 0.03); + } + + &__item:hover { + background-color: rgba(0, 0, 0, 0.06); + } + + &__item__index { + display: flex; + width: 24px; + } + + &__item__radio { + display: flex; + flex: 1; + + input { + padding: 10px; + margin-top: 13px; + } + } + + &__item__label { + display: flex; + flex: 1; + padding-left: 10px; + padding-top: 10px; + padding-bottom: 10px; + } + + &__item__balance { + display: flex; + flex: 1; + justify-content: center; + } + + &__item__link { + display: flex; + margin-top: 13px; + } + + &__item__link img { + width: 15px; + height: 15px; + } +} + +.hw-list-pagination { + display: flex; + align-self: flex-end; + margin-top: 10px; + + &__button { + height: 19px; + display: flex; + color: #33a4e7; + font-size: 14px; + line-height: 19px; + border: none; + min-width: 46px; + margin-right: 0px; + margin-left: 16px; + padding: 0px; + text-transform: uppercase; + font-family: Roboto; + } +} + +.new-account-connect-form { + display: flex; + flex-flow: column; + align-items: center; + padding: 15px 30px 0; + height: 710px; + overflow: auto; + + &.unsupported-browser { + height: 210px; + } + + &.account-list { + height: auto; + } + + &__buttons { + margin-top: 39px; + display: flex; + width: 100%; + justify-content: space-between; + } + + &__button { + width: 150px; + min-width: initial; + } + + .btn-primary { + background-color: #259DE5; + color: #FFFFFF; + border: none; + width: 100%; + min-height: 54px; + font-weight: 300; + font-size: 14px; + } + + &__button.unlock { + width: 50%; + } + + &__button.btn-primary--disabled { + cursor: not-allowed; + opacity: .5; + } +} + +.hw-forget-device-container { + display: flex; + flex-flow: column; + align-items: center; + padding: 22px; + + a { + color: #2f9ae0; + font-size: 14px; + cursor: pointer; + } +} + .new-account-create-form { display: flex; flex-flow: column; align-items: center; - padding: 30px 30px 0; + padding: 30px; &__input-label { color: $scorpion; diff --git a/ui/app/css/itcss/components/send.scss b/ui/app/css/itcss/components/send.scss index c168242cf..e9c872ea7 100644 --- a/ui/app/css/itcss/components/send.scss +++ b/ui/app/css/itcss/components/send.scss @@ -628,7 +628,7 @@ } } - &__to-autocomplete, &__memo-text-area { + &__to-autocomplete, &__memo-text-area, &__hex-data { &__input { height: 54px; width: 100%; @@ -899,4 +899,4 @@ .sliders-icon { color: $curious-blue; -}
\ No newline at end of file +} diff --git a/ui/app/css/itcss/components/transaction-list.scss b/ui/app/css/itcss/components/transaction-list.scss index d03faf486..1d45ff13b 100644 --- a/ui/app/css/itcss/components/transaction-list.scss +++ b/ui/app/css/itcss/components/transaction-list.scss @@ -129,12 +129,14 @@ .tx-list-item-retry-container { background: #d1edff; width: 100%; - border-radius: 4px; - font-size: 0.8em; + border-radius: 12px; + font-size: .75rem; display: flex; justify-content: center; margin-left: 44px; width: calc(100% - 44px); + padding: 4px; + cursor: pointer; @media screen and (min-width: 576px) and (max-width: 679px) { flex-flow: column; @@ -151,10 +153,6 @@ } } -.tx-list-item-retry-copy { - font-family: Roboto; -} - .tx-list-item-retry-link { text-decoration: underline; margin-left: 6px; diff --git a/ui/app/helpers/confirm-transaction/util.js b/ui/app/helpers/confirm-transaction/util.js index ad247a348..1373d28df 100644 --- a/ui/app/helpers/confirm-transaction/util.js +++ b/ui/app/helpers/confirm-transaction/util.js @@ -114,3 +114,20 @@ export function formatCurrency (value, currencyCode) { ? currencyFormatter.format(Number(value), { code: upperCaseCurrencyCode }) : value } + +export function convertTokenToFiat ({ + value, + toCurrency, + conversionRate, + contractExchangeRate, +}) { + const totalExchangeRate = conversionRate * contractExchangeRate + + return conversionUtil(value, { + fromNumericBase: 'dec', + toNumericBase: 'dec', + toCurrency, + numberOfDecimals: 2, + conversionRate: totalExchangeRate, + }) +} diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index f453812b9..50d8bcba7 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -49,6 +49,8 @@ function reduceApp (state, action) { }, }, sidebarOpen: false, + alertOpen: false, + alertMessage: null, networkDropdownOpen: false, currentView: seedWords ? seedConfView : defaultView, accountDetail: { @@ -88,6 +90,19 @@ function reduceApp (state, action) { sidebarOpen: false, }) + // sidebar methods + case actions.ALERT_OPEN: + return extend(appState, { + alertOpen: true, + alertMessage: action.value, + }) + + case actions.ALERT_CLOSE: + return extend(appState, { + alertOpen: false, + alertMessage: null, + }) + // modal methods: case actions.MODAL_OPEN: const { name, ...modalProps } = action.payload diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 6c8ac9ed7..3f1d3394f 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -222,6 +222,14 @@ function reduceMetamask (state, action) { }, }) + case actions.UPDATE_SEND_HEX_DATA: + return extend(metamaskState, { + send: { + ...metamaskState.send, + data: action.value, + }, + }) + case actions.UPDATE_SEND_FROM: return extend(metamaskState, { send: { diff --git a/ui/app/routes.js b/ui/app/routes.js index 7ac606b1a..f6b2a7a55 100644 --- a/ui/app/routes.js +++ b/ui/app/routes.js @@ -9,6 +9,7 @@ const ADD_TOKEN_ROUTE = '/add-token' const CONFIRM_ADD_TOKEN_ROUTE = '/confirm-add-token' const NEW_ACCOUNT_ROUTE = '/new-account' const IMPORT_ACCOUNT_ROUTE = '/new-account/import' +const CONNECT_HARDWARE_ROUTE = '/new-account/connect' const SEND_ROUTE = '/send' const NOTICE_ROUTE = '/notice' const WELCOME_ROUTE = '/welcome' @@ -26,6 +27,7 @@ const CONFIRM_SEND_ETHER_PATH = '/send-ether' const CONFIRM_SEND_TOKEN_PATH = '/send-token' const CONFIRM_DEPLOY_CONTRACT_PATH = '/deploy-contract' const CONFIRM_APPROVE_PATH = '/approve' +const CONFIRM_TRANSFER_FROM_PATH = '/transfer-from' const CONFIRM_TOKEN_METHOD_PATH = '/token-method' const SIGNATURE_REQUEST_PATH = '/signature-request' @@ -41,6 +43,7 @@ module.exports = { CONFIRM_ADD_TOKEN_ROUTE, NEW_ACCOUNT_ROUTE, IMPORT_ACCOUNT_ROUTE, + CONNECT_HARDWARE_ROUTE, SEND_ROUTE, NOTICE_ROUTE, WELCOME_ROUTE, @@ -57,6 +60,7 @@ module.exports = { CONFIRM_SEND_TOKEN_PATH, CONFIRM_DEPLOY_CONTRACT_PATH, CONFIRM_APPROVE_PATH, + CONFIRM_TRANSFER_FROM_PATH, CONFIRM_TOKEN_METHOD_PATH, SIGNATURE_REQUEST_PATH, } diff --git a/ui/app/selectors/confirm-transaction.js b/ui/app/selectors/confirm-transaction.js index cde83804d..54016a30e 100644 --- a/ui/app/selectors/confirm-transaction.js +++ b/ui/app/selectors/confirm-transaction.js @@ -1,5 +1,6 @@ import { createSelector } from 'reselect' import txHelper from '../../lib/tx-helper' +import { calcTokenAmount } from '../token-util' const unapprovedTxsSelector = state => state.metamask.unapprovedTxs const unapprovedMsgsSelector = state => state.metamask.unapprovedMsgs @@ -63,3 +64,101 @@ export const unconfirmedTransactionsHashSelector = createSelector( export const currentCurrencySelector = state => state.metamask.currentCurrency export const conversionRateSelector = state => state.metamask.conversionRate + +const txDataSelector = state => state.confirmTransaction.txData +const tokenDataSelector = state => state.confirmTransaction.tokenData +const tokenPropsSelector = state => state.confirmTransaction.tokenProps + +const contractExchangeRatesSelector = state => state.metamask.contractExchangeRates + +const tokenDecimalsSelector = createSelector( + tokenPropsSelector, + tokenProps => tokenProps && tokenProps.tokenDecimals +) + +const tokenDataParamsSelector = createSelector( + tokenDataSelector, + tokenData => tokenData && tokenData.params || [] +) + +const txParamsSelector = createSelector( + txDataSelector, + txData => txData && txData.txParams || {} +) + +export const tokenAddressSelector = createSelector( + txParamsSelector, + txParams => txParams && txParams.to +) + +const TOKEN_PARAM_SPENDER = '_spender' +const TOKEN_PARAM_TO = '_to' +const TOKEN_PARAM_VALUE = '_value' + +export const tokenAmountAndToAddressSelector = createSelector( + tokenDataParamsSelector, + params => { + let toAddress = '' + let tokenAmount = 0 + + if (params && params.length) { + const toParam = params.find(param => param.name === TOKEN_PARAM_TO) + const valueParam = params.find(param => param.name === TOKEN_PARAM_VALUE) + toAddress = toParam ? toParam.value : params[0].value + tokenAmount = valueParam ? Number(valueParam.value) : Number(params[1].value) + } + + return { + toAddress, + tokenAmount, + } + } +) + +export const approveTokenAmountAndToAddressSelector = createSelector( + tokenDataParamsSelector, + params => { + let toAddress = '' + let tokenAmount = 0 + + if (params && params.length) { + toAddress = params.find(param => param.name === TOKEN_PARAM_SPENDER).value + tokenAmount = Number(params.find(param => param.name === TOKEN_PARAM_VALUE).value) + } + + return { + toAddress, + tokenAmount, + } + } +) + +export const sendTokenTokenAmountAndToAddressSelector = createSelector( + tokenDataParamsSelector, + tokenDecimalsSelector, + (params, tokenDecimals) => { + let toAddress = '' + let tokenAmount = 0 + + if (params && params.length) { + toAddress = params.find(param => param.name === TOKEN_PARAM_TO).value + tokenAmount = Number(params.find(param => param.name === TOKEN_PARAM_VALUE).value) + + if (tokenDecimals) { + tokenAmount = calcTokenAmount(tokenAmount, tokenDecimals) + } + } + + return { + toAddress, + tokenAmount, + } + } +) + + +export const contractExchangeRateSelector = createSelector( + contractExchangeRatesSelector, + tokenAddressSelector, + (contractExchangeRates, tokenAddress) => contractExchangeRates[tokenAddress] +) diff --git a/ui/app/util.js b/ui/app/util.js index 8c85c5926..8b194e0c7 100644 --- a/ui/app/util.js +++ b/ui/app/util.js @@ -59,6 +59,7 @@ module.exports = { allNull, getTokenAddressFromTokenObject, checksumAddress, + addressSlicer, } function valuesFor (obj) { @@ -303,3 +304,11 @@ function getTokenAddressFromTokenObject (token) { function checksumAddress (address) { return address ? ethUtil.toChecksumAddress(address) : '' } + +function addressSlicer (address = '') { + if (address.length < 11) { + return address + } + + return `${address.slice(0, 6)}...${address.slice(-4)}` +} |