diff options
50 files changed, 405 insertions, 98 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 097d57cdf..4b7ea17a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,13 @@ ## Current Develop Branch -- (#4606)[https://github.com/MetaMask/metamask-extension/pull/4606]: Add new metamask_watchAsset method. +- [#4606](https://github.com/MetaMask/metamask-extension/pull/4606): Add new metamask_watchAsset method. ## 4.9.3 Wed Aug 15 2018 -- (#4897)[https://github.com/MetaMask/metamask-extension/pull/4897]: QR code scan for recipient addresses. -- (#4961)[https://github.com/MetaMask/metamask-extension/pull/4961]: Add a download seed phrase link. -- (#5060)[https://github.com/MetaMask/metamask-extension/pull/5060]: Fix bug where gas was not updating properly. +- [#4897](https://github.com/MetaMask/metamask-extension/pull/4897): QR code scan for recipient addresses. +- [#4961](https://github.com/MetaMask/metamask-extension/pull/4961): Add a download seed phrase link. +- [#5060](https://github.com/MetaMask/metamask-extension/pull/5060): Fix bug where gas was not updating properly. ## 4.9.2 Mon Aug 09 2018 diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 3cc8dae34..14e867b33 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -882,6 +882,12 @@ "secretPhrase": { "message": "Enter your secret twelve word phrase here to restore your vault." }, + "showHexData": { + "message": "Show Hex Data" + }, + "showHexDataDescription": { + "message": "Select this to show the hex data field on the send screen" + }, "newPassword8Chars": { "message": "New Password (min 8 chars)" }, diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index 1463e2b5f..6f850d89b 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -490,7 +490,7 @@ "message": "Sélectionner un service" }, "send": { - "message": "Envoyé" + "message": "Envoyer" }, "sendTokens": { "message": "Envoyer des jetons" diff --git a/development/states/add-token.json b/development/states/add-token.json index 84ad5dd4c..d04b3a3ca 100644 --- a/development/states/add-token.json +++ b/development/states/add-token.json @@ -123,6 +123,7 @@ "modalState": {}, "previousModalState": {} }, + "sidebar": {}, "transForward": true, "isLoading": false, "warning": null, diff --git a/development/states/confirm-new-ui.json b/development/states/confirm-new-ui.json index 2c2e17704..fffee9893 100644 --- a/development/states/confirm-new-ui.json +++ b/development/states/confirm-new-ui.json @@ -141,6 +141,7 @@ "accountDetail": { "subview": "transactions" }, + "sidebar": {}, "modal": { "modalState": {}, "previousModalState": {} diff --git a/development/states/confirm-sig-requests.json b/development/states/confirm-sig-requests.json index 829f513a8..5017a4d57 100644 --- a/development/states/confirm-sig-requests.json +++ b/development/states/confirm-sig-requests.json @@ -162,6 +162,7 @@ "accountDetail": { "subview": "transactions" }, + "sidebar": {}, "modal": { "modalState": {}, "previousModalState": {} diff --git a/development/states/currency-localization.json b/development/states/currency-localization.json index 6848c0840..847ea11a3 100644 --- a/development/states/currency-localization.json +++ b/development/states/currency-localization.json @@ -120,6 +120,7 @@ "accountDetail": { "subview": "transactions" }, + "sidebar": {}, "modal": { "modalState": {}, "previousModalState": {} diff --git a/development/states/first-time.json b/development/states/first-time.json index f44148973..a31b985a3 100644 --- a/development/states/first-time.json +++ b/development/states/first-time.json @@ -48,6 +48,7 @@ "accountDetail": { "subview": "transactions" }, + "sidebar": {}, "transForward": true, "isLoading": false, "warning": null, diff --git a/development/states/send-edit.json b/development/states/send-edit.json index 8e5c25a82..6330b777d 100644 --- a/development/states/send-edit.json +++ b/development/states/send-edit.json @@ -22,6 +22,7 @@ "name": "Send Account 4" } }, + "assetImages": {}, "unapprovedTxs": {}, "currentCurrency": "USD", "conversionRate": 1200.88200327, @@ -141,6 +142,7 @@ "accountDetail": { "subview": "transactions" }, + "sidebar": {}, "modal": { "modalState": {}, "previousModalState": {} diff --git a/development/states/send-new-ui.json b/development/states/send-new-ui.json index ad2ff3d6e..bb4847155 100644 --- a/development/states/send-new-ui.json +++ b/development/states/send-new-ui.json @@ -61,6 +61,7 @@ "name": "Address Book Account 1" } ], + "assetImages": {}, "tokens": [], "transactions": {}, "selectedAddressTxList": [], @@ -120,6 +121,7 @@ "accountDetail": { "subview": "transactions" }, + "sidebar": {}, "modal": { "modalState": {}, "previousModalState": {} diff --git a/development/states/send.json b/development/states/send.json index 73ac62f65..4c67f8ac6 100644 --- a/development/states/send.json +++ b/development/states/send.json @@ -21,6 +21,7 @@ "name": "Account 4" } }, + "assetImages": {}, "unapprovedTxs": {}, "currentCurrency": "USD", "conversionRate": 16.88200327, @@ -99,6 +100,7 @@ "accountExport": "none", "privateKey": "" }, + "sidebar": {}, "transForward": true, "isLoading": false, "warning": null, diff --git a/development/states/tx-list-items.json b/development/states/tx-list-items.json index f22fd0a56..0d2273cb0 100644 --- a/development/states/tx-list-items.json +++ b/development/states/tx-list-items.json @@ -118,6 +118,7 @@ "modalState": {}, "previousModalState": {} }, + "sidebar": {}, "transForward": true, "isLoading": false, "warning": null, diff --git a/package.json b/package.json index 8aea7025c..0ef2ab6a9 100644 --- a/package.json +++ b/package.json @@ -251,8 +251,8 @@ "del": "^3.0.0", "dot-only-hunter": "^1.0.3", "envify": "^4.0.0", - "enzyme": "^3.3.0", - "enzyme-adapter-react-15": "^1.0.5", + "enzyme": "^3.4.4", + "enzyme-adapter-react-15": "^1.0.6", "eslint-plugin-chai": "0.0.1", "eslint-plugin-json": "^1.2.0", "eslint-plugin-mocha": "^5.0.0", diff --git a/ui/app/actions.js b/ui/app/actions.js index 845eafdf6..6d5b1ef3f 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -1853,9 +1853,13 @@ function hideModal (payload) { } } -function showSidebar () { +function showSidebar ({ transitionName, type }) { return { type: actions.SIDEBAR_OPEN, + value: { + transitionName, + type, + }, } } diff --git a/ui/app/app.js b/ui/app/app.js index e0bdac359..aa051280b 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -15,7 +15,7 @@ const SendTransactionScreen = require('./components/send/send.container') const ConfirmTransaction = require('./components/pages/confirm-transaction') // slideout menu -const WalletView = require('./components/wallet-view') +const Sidebar = require('./components/sidebars').default // other views import Home from './components/pages/home' @@ -31,7 +31,6 @@ const CreateAccountPage = require('./components/pages/create-account') const NoticeScreen = require('./components/pages/notice') const Loading = require('./components/loading-screen') -const ReactCSSTransitionGroup = require('react-addons-css-transition-group') const NetworkDropdown = require('./components/dropdowns/network-dropdown') const AccountMenu = require('./components/account-menu') @@ -105,6 +104,7 @@ class App extends Component { frequentRpcList, currentView, setMouseUserState, + sidebar, } = this.props const isLoadingNetwork = network === 'loading' && currentView.name !== 'config' const loadMessage = loadingMessage || isLoadingNetwork ? @@ -137,7 +137,12 @@ class App extends Component { h(AppHeader), // sidebar - this.renderSidebar(), + h(Sidebar, { + sidebarOpen: sidebar.isOpen, + hideSidebar: this.props.hideSidebar, + transitionName: sidebar.transitionName, + type: sidebar.type, + }), // network dropdown h(NetworkDropdown, { @@ -157,51 +162,6 @@ class App extends Component { ) } - renderSidebar () { - return h('div', [ - h('style', ` - .sidebar-enter { - transition: transform 300ms ease-in-out; - transform: translateX(-100%); - } - .sidebar-enter.sidebar-enter-active { - transition: transform 300ms ease-in-out; - transform: translateX(0%); - } - .sidebar-leave { - transition: transform 200ms ease-out; - transform: translateX(0%); - } - .sidebar-leave.sidebar-leave-active { - transition: transform 200ms ease-out; - transform: translateX(-100%); - } - `), - - h(ReactCSSTransitionGroup, { - transitionName: 'sidebar', - transitionEnterTimeout: 300, - transitionLeaveTimeout: 200, - }, [ - // A second instance of Walletview is used for non-mobile viewports - this.props.sidebarOpen ? h(WalletView, { - responsiveDisplayClassname: 'sidebar', - style: {}, - }) : undefined, - - ]), - - // overlay - // TODO: add onClick for overlay to close sidebar - this.props.sidebarOpen ? h('div.sidebar-overlay', { - style: {}, - onClick: () => { - this.props.hideSidebar() - }, - }, []) : undefined, - ]) - } - toggleMetamaskActive () { if (!this.props.isUnlocked) { // currently inactive: redirect to password box @@ -270,7 +230,7 @@ App.propTypes = { provider: PropTypes.object, frequentRpcList: PropTypes.array, currentView: PropTypes.object, - sidebarOpen: PropTypes.bool, + sidebar: PropTypes.object, alertOpen: PropTypes.bool, hideSidebar: PropTypes.func, isMascara: PropTypes.bool, @@ -306,7 +266,7 @@ function mapStateToProps (state) { const { appState, metamask } = state const { networkDropdownOpen, - sidebarOpen, + sidebar, alertOpen, alertMessage, isLoading, @@ -333,7 +293,7 @@ function mapStateToProps (state) { return { // state from plugin networkDropdownOpen, - sidebarOpen, + sidebar, alertOpen, alertMessage, isLoading, diff --git a/ui/app/components/balance-component.js b/ui/app/components/balance-component.js index 753a27b06..d63d78c9f 100644 --- a/ui/app/components/balance-component.js +++ b/ui/app/components/balance-component.js @@ -5,6 +5,7 @@ const inherits = require('util').inherits const TokenBalance = require('./token-balance') const Identicon = require('./identicon') import CurrencyDisplay from './currency-display' +const { getAssetImages, conversionRateSelector, getCurrentCurrency} = require('../selectors') const { formatBalance, generateBalanceObject } = require('../util') @@ -19,9 +20,9 @@ function mapStateToProps (state) { return { account, network, - conversionRate: state.metamask.conversionRate, - currentCurrency: state.metamask.currentCurrency, - assetImages: state.metamask.assetImages, + conversionRate: conversionRateSelector(state), + currentCurrency: getCurrentCurrency(state), + assetImages: getAssetImages(state), } } diff --git a/ui/app/components/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js b/ui/app/components/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js index 08923af88..de9aa6eb7 100644 --- a/ui/app/components/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js +++ b/ui/app/components/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js @@ -18,6 +18,7 @@ export default class ConfirmPageContainerContent extends Component { hideSubtitle: PropTypes.bool, identiconAddress: PropTypes.string, nonce: PropTypes.string, + assetImage: PropTypes.string, subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), summaryComponent: PropTypes.node, title: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), @@ -60,6 +61,7 @@ export default class ConfirmPageContainerContent extends Component { hideSubtitle, identiconAddress, nonce, + assetImage, summaryComponent, detailsComponent, dataComponent, @@ -85,6 +87,7 @@ export default class ConfirmPageContainerContent extends Component { hideSubtitle={hideSubtitle} identiconAddress={identiconAddress} nonce={nonce} + assetImage={assetImage} /> ) } diff --git a/ui/app/components/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js b/ui/app/components/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js index 3b1ee62c5..38b158fd3 100644 --- a/ui/app/components/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js +++ b/ui/app/components/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js @@ -4,7 +4,7 @@ import classnames from 'classnames' import Identicon from '../../../identicon' const ConfirmPageContainerSummary = props => { - const { action, title, subtitle, hideSubtitle, className, identiconAddress, nonce } = props + const { action, title, subtitle, hideSubtitle, className, identiconAddress, nonce, assetImage } = props return ( <div className={classnames('confirm-page-container-summary', className)}> @@ -27,6 +27,7 @@ const ConfirmPageContainerSummary = props => { className="confirm-page-container-summary__identicon" diameter={36} address={identiconAddress} + image={assetImage} /> ) } @@ -51,6 +52,7 @@ ConfirmPageContainerSummary.propTypes = { className: PropTypes.string, identiconAddress: PropTypes.string, nonce: PropTypes.string, + assetImage: PropTypes.string, } export default ConfirmPageContainerSummary diff --git a/ui/app/components/confirm-page-container/confirm-page-container.component.js b/ui/app/components/confirm-page-container/confirm-page-container.component.js index 24ff05353..b1582051e 100644 --- a/ui/app/components/confirm-page-container/confirm-page-container.component.js +++ b/ui/app/components/confirm-page-container/confirm-page-container.component.js @@ -38,6 +38,7 @@ export default class ConfirmPageContainer extends Component { detailsComponent: PropTypes.node, identiconAddress: PropTypes.string, nonce: PropTypes.string, + assetImage: PropTypes.string, summaryComponent: PropTypes.node, warning: PropTypes.string, // Footer @@ -70,8 +71,10 @@ export default class ConfirmPageContainer extends Component { onSubmit, identiconAddress, nonce, + assetImage, warning, } = this.props + const renderAssetImage = contentComponent || (!contentComponent && !identiconAddress) return ( <div className="page-container"> @@ -84,6 +87,7 @@ export default class ConfirmPageContainer extends Component { senderAddress={fromAddress} recipientName={toName} recipientAddress={toAddress} + assetImage={renderAssetImage ? assetImage : undefined} /> </ConfirmPageContainerHeader> { @@ -101,6 +105,7 @@ export default class ConfirmPageContainer extends Component { errorKey={errorKey} identiconAddress={identiconAddress} nonce={nonce} + assetImage={assetImage} warning={warning} /> ) diff --git a/ui/app/components/dropdowns/components/account-dropdowns.js b/ui/app/components/dropdowns/components/account-dropdowns.js index 179b6617f..b497f5c09 100644 --- a/ui/app/components/dropdowns/components/account-dropdowns.js +++ b/ui/app/components/dropdowns/components/account-dropdowns.js @@ -459,7 +459,7 @@ const mapDispatchToProps = (dispatch) => { function mapStateToProps (state) { return { keyrings: state.metamask.keyrings, - sidebarOpen: state.appState.sidebarOpen, + sidebarOpen: state.appState.sidebar.isOpen, } } diff --git a/ui/app/components/index.scss b/ui/app/components/index.scss index bdcb5626c..cb4065fd9 100644 --- a/ui/app/components/index.scss +++ b/ui/app/components/index.scss @@ -33,3 +33,7 @@ @import './transaction-list-item/index'; @import './transaction-status/index'; + +@import './app-header/index'; + +@import './sidebars/index'; diff --git a/ui/app/components/menu-bar/menu-bar.container.js b/ui/app/components/menu-bar/menu-bar.container.js index 2bd0ed6ed..ae32882ae 100644 --- a/ui/app/components/menu-bar/menu-bar.container.js +++ b/ui/app/components/menu-bar/menu-bar.container.js @@ -3,17 +3,22 @@ import MenuBar from './menu-bar.component' import { showSidebar, hideSidebar } from '../../actions' const mapStateToProps = state => { - const { appState: { sidebarOpen, isMascara } } = state + const { appState: { sidebar: { isOpen }, isMascara } } = state return { - sidebarOpen, + sidebarOpen: isOpen, isMascara, } } const mapDispatchToProps = dispatch => { return { - showSidebar: () => dispatch(showSidebar()), + showSidebar: () => { + dispatch(showSidebar({ + transitionName: 'sidebar-right', + type: 'wallet-view', + })) + }, hideSidebar: () => dispatch(hideSidebar()), } } 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 3216d01c3..56cfbccc8 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 @@ -38,6 +38,7 @@ export default class ConfirmTransactionBase extends Component { isTxReprice: PropTypes.bool, methodData: PropTypes.object, nonce: PropTypes.string, + assetImage: PropTypes.string, sendTransaction: PropTypes.func, showCustomizeGasModal: PropTypes.func, showTransactionConfirmedModal: PropTypes.func, @@ -310,6 +311,7 @@ export default class ConfirmTransactionBase extends Component { contentComponent, onEdit, nonce, + assetImage, warning, } = this.props const { submitting, submitError } = this.state @@ -334,6 +336,7 @@ export default class ConfirmTransactionBase extends Component { dataComponent={this.renderData()} contentComponent={contentComponent} nonce={nonce} + assetImage={assetImage} identiconAddress={identiconAddress} errorMessage={errorMessage || submitError} errorKey={propsErrorKey || errorKey} 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 0c0deff18..8f54c8040 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 @@ -52,8 +52,9 @@ const mapStateToProps = (state, props) => { accounts, selectedAddress, selectedAddressTxList, + assetImages, } = metamask - + const assetImage = assetImages[txParamsToAddress] const { balance } = accounts[selectedAddress] const { name: fromName } = identities[selectedAddress] const toAddress = propsToAddress || txParamsToAddress @@ -88,6 +89,7 @@ const mapStateToProps = (state, props) => { conversionRate, transactionStatus, nonce, + assetImage, } } diff --git a/ui/app/components/pages/settings/settings.js b/ui/app/components/pages/settings/settings.js index ff42a13de..a5ea1b89c 100644 --- a/ui/app/components/pages/settings/settings.js +++ b/ui/app/components/pages/settings/settings.js @@ -66,6 +66,30 @@ class Settings extends Component { ]) } + renderHexDataOptIn () { + const { metamask: { featureFlags: { sendHexData } }, setHexDataFeatureFlag } = this.props + + return h('div.settings__content-row', [ + h('div.settings__content-item', [ + h('span', this.context.t('showHexData')), + h( + 'div.settings__content-description', + this.context.t('showHexDataDescription') + ), + ]), + h('div.settings__content-item', [ + h('div.settings__content-item-col', [ + h(ToggleButton, { + value: sendHexData, + onToggle: (value) => setHexDataFeatureFlag(!value), + activeLabel: '', + inactiveLabel: '', + }), + ]), + ]), + ]) + } + renderCurrentConversion () { const { metamask: { currentCurrency, conversionDate }, setCurrentCurrency } = this.props @@ -307,6 +331,7 @@ class Settings extends Component { !isMascara && this.renderOldUI(), this.renderResetAccount(), this.renderBlockieOptIn(), + this.renderHexDataOptIn(), ]) ) } @@ -315,6 +340,7 @@ class Settings extends Component { Settings.propTypes = { metamask: PropTypes.object, setUseBlockie: PropTypes.func, + setHexDataFeatureFlag: PropTypes.func, setCurrentCurrency: PropTypes.func, setRpcTarget: PropTypes.func, displayWarning: PropTypes.func, @@ -349,6 +375,9 @@ const mapDispatchToProps = dispatch => { setFeatureFlagToBeta: () => { return dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL')) }, + setHexDataFeatureFlag: (featureFlagShowState) => { + return dispatch(actions.setFeatureFlag('sendHexData', featureFlagShowState)) + }, showResetAccountConfirmationModal: () => { return dispatch(actions.showModal({ name: 'CONFIRM_RESET_ACCOUNT' })) }, 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 df7bcb7cc..9e0ce9c23 100644 --- a/ui/app/components/send/send-content/send-content.component.js +++ b/ui/app/components/send/send-content/send-content.component.js @@ -12,6 +12,7 @@ export default class SendContent extends Component { static propTypes = { updateGas: PropTypes.func, scanQrCode: PropTypes.func, + showHexData: PropTypes.bool, }; render () { @@ -25,7 +26,7 @@ export default class SendContent extends Component { /> <SendAmountRow updateGas={(updateData) => this.props.updateGas(updateData)} /> <SendGasRow /> - <SendHexDataRow /> + { this.props.showHexData ? <SendHexDataRow /> : null } </div> </PageContainerContent> ) 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..7c3a2cc2d 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 @@ -8,12 +8,13 @@ import SendAmountRow from '../send-amount-row/send-amount-row.container' import SendFromRow from '../send-from-row/send-from-row.container' import SendGasRow from '../send-gas-row/send-gas-row.container' import SendToRow from '../send-to-row/send-to-row.container' +import SendHexDataRow from '../send-hex-data-row/send-hex-data-row.container' describe('SendContent Component', function () { let wrapper beforeEach(() => { - wrapper = shallow(<SendContent />) + wrapper = shallow(<SendContent showHexData={true} />) }) describe('render', () => { @@ -33,6 +34,17 @@ describe('SendContent Component', function () { assert(PageContainerContentChild.childAt(1).is(SendToRow)) assert(PageContainerContentChild.childAt(2).is(SendAmountRow)) assert(PageContainerContentChild.childAt(3).is(SendGasRow)) + assert(PageContainerContentChild.childAt(4).is(SendHexDataRow)) + }) + + it('should not render the SendHexDataRow if props.showHexData is false', () => { + wrapper.setProps({ showHexData: false }) + const PageContainerContentChild = wrapper.find(PageContainerContent).children() + assert(PageContainerContentChild.childAt(0).is(SendFromRow)) + assert(PageContainerContentChild.childAt(1).is(SendToRow)) + assert(PageContainerContentChild.childAt(2).is(SendAmountRow)) + assert(PageContainerContentChild.childAt(3).is(SendGasRow)) + assert.equal(PageContainerContentChild.childAt(4).html(), null) }) }) }) diff --git a/ui/app/components/send/send.component.js b/ui/app/components/send/send.component.js index 0d8ffd179..0dc973632 100644 --- a/ui/app/components/send/send.component.js +++ b/ui/app/components/send/send.component.js @@ -193,7 +193,7 @@ export default class SendTransactionScreen extends PersistentForm { } render () { - const { history } = this.props + const { history, showHexData } = this.props return ( <div className="page-container"> @@ -201,6 +201,7 @@ export default class SendTransactionScreen extends PersistentForm { <SendContent updateGas={(updateData) => this.updateGas(updateData)} scanQrCode={_ => this.props.scanQrCode()} + showHexData={showHexData} /> <SendFooter history={history}/> </div> diff --git a/ui/app/components/send/send.container.js b/ui/app/components/send/send.container.js index 41735de64..6ee8de9aa 100644 --- a/ui/app/components/send/send.container.js +++ b/ui/app/components/send/send.container.js @@ -18,6 +18,7 @@ import { getSelectedTokenToFiatRate, getSendAmount, getSendEditingTransactionId, + getSendHexDataFeatureFlagState, getSendFromObject, getSendTo, getTokenBalance, @@ -64,6 +65,7 @@ function mapStateToProps (state) { recentBlocks: getRecentBlocks(state), selectedAddress: getSelectedAddress(state), selectedToken: getSelectedToken(state), + showHexData: getSendHexDataFeatureFlagState(state), to: getSendTo(state), tokenBalance: getTokenBalance(state), tokenContract: getSelectedTokenContract(state), diff --git a/ui/app/components/send/send.selectors.js b/ui/app/components/send/send.selectors.js index ab3f6d34b..22e379693 100644 --- a/ui/app/components/send/send.selectors.js +++ b/ui/app/components/send/send.selectors.js @@ -34,6 +34,7 @@ const selectors = { getSelectedTokenToFiatRate, getSendAmount, getSendHexData, + getSendHexDataFeatureFlagState, getSendEditingTransactionId, getSendErrors, getSendFrom, @@ -216,6 +217,10 @@ function getSendHexData (state) { return state.metamask.send.data } +function getSendHexDataFeatureFlagState (state) { + return state.metamask.featureFlags.sendHexData +} + function getSendEditingTransactionId (state) { return state.metamask.send.editingTransactionId } diff --git a/ui/app/components/send/tests/send-component.test.js b/ui/app/components/send/tests/send-component.test.js index 6194ec508..d2c2ee926 100644 --- a/ui/app/components/send/tests/send-component.test.js +++ b/ui/app/components/send/tests/send-component.test.js @@ -47,6 +47,7 @@ describe('Send Component', function () { recentBlocks={['mockBlock']} selectedAddress={'mockSelectedAddress'} selectedToken={'mockSelectedToken'} + showHexData={true} tokenBalance={'mockTokenBalance'} tokenContract={'mockTokenContract'} updateAndSetGasTotal={propsMethodSpies.updateAndSetGasTotal} @@ -328,5 +329,9 @@ describe('Send Component', function () { } ) }) + + it('should pass showHexData to SendContent', () => { + assert.equal(wrapper.find(SendContent).props().showHexData, true) + }) }) }) diff --git a/ui/app/components/send/tests/send-container.test.js b/ui/app/components/send/tests/send-container.test.js index 57e332780..85eec6a53 100644 --- a/ui/app/components/send/tests/send-container.test.js +++ b/ui/app/components/send/tests/send-container.test.js @@ -39,6 +39,7 @@ proxyquire('../send.container.js', { getSelectedToken: (s) => `mockSelectedToken:${s}`, getSelectedTokenContract: (s) => `mockTokenContract:${s}`, getSelectedTokenToFiatRate: (s) => `mockTokenToFiatRate:${s}`, + getSendHexDataFeatureFlagState: (s) => `mockSendHexDataFeatureFlagState:${s}`, getSendAmount: (s) => `mockAmount:${s}`, getSendTo: (s) => `mockTo:${s}`, getSendEditingTransactionId: (s) => `mockEditingTransactionId:${s}`, @@ -73,6 +74,7 @@ describe('send container', () => { recentBlocks: 'mockRecentBlocks:mockState', selectedAddress: 'mockSelectedAddress:mockState', selectedToken: 'mockSelectedToken:mockState', + showHexData: 'mockSendHexDataFeatureFlagState:mockState', to: 'mockTo:mockState', tokenBalance: 'mockTokenBalance:mockState', tokenContract: 'mockTokenContract:mockState', 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..8b939dadb 100644 --- a/ui/app/components/send/tests/send-selectors-test-data.js +++ b/ui/app/components/send/tests/send-selectors-test-data.js @@ -2,7 +2,7 @@ module.exports = { 'metamask': { 'isInitialized': true, 'isUnlocked': true, - 'featureFlags': {'betaUI': true}, + 'featureFlags': {'betaUI': true, 'sendHexData': true}, 'rpcTarget': 'https://rawtestrpc.metamask.io/', 'identities': { '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825': { diff --git a/ui/app/components/send/tests/send-selectors.test.js b/ui/app/components/send/tests/send-selectors.test.js index 218da656b..1a47cd209 100644 --- a/ui/app/components/send/tests/send-selectors.test.js +++ b/ui/app/components/send/tests/send-selectors.test.js @@ -31,6 +31,7 @@ const { getSendFrom, getSendFromBalance, getSendFromObject, + getSendHexDataFeatureFlagState, getSendMaxModeState, getSendTo, getSendToAccounts, @@ -379,6 +380,15 @@ describe('send selectors', () => { }) }) + describe('getSendHexDataFeatureFlagState()', () => { + it('should return the sendHexData feature flag state', () => { + assert.deepEqual( + getSendHexDataFeatureFlagState(mockState), + true + ) + }) + }) + describe('getSendFrom()', () => { it('should return the send.from', () => { assert.deepEqual( diff --git a/ui/app/components/sender-to-recipient/sender-to-recipient.component.js b/ui/app/components/sender-to-recipient/sender-to-recipient.component.js index 5af4045f5..445a11d8a 100644 --- a/ui/app/components/sender-to-recipient/sender-to-recipient.component.js +++ b/ui/app/components/sender-to-recipient/sender-to-recipient.component.js @@ -20,6 +20,7 @@ export default class SenderToRecipient extends PureComponent { t: PropTypes.func, variant: PropTypes.oneOf([DEFAULT_VARIANT, CARDS_VARIANT]), addressOnly: PropTypes.bool, + assetImage: PropTypes.string, } static defaultProps = { @@ -66,13 +67,14 @@ export default class SenderToRecipient extends PureComponent { } renderRecipientIdenticon () { - const { recipientAddress } = this.props + const { recipientAddress, assetImage } = this.props return !this.props.addressOnly && ( <div className="sender-to-recipient__sender-icon"> <Identicon address={recipientAddress} diameter={24} + image={assetImage} /> </div> ) diff --git a/ui/app/components/sidebars/index.js b/ui/app/components/sidebars/index.js new file mode 100644 index 000000000..732925f69 --- /dev/null +++ b/ui/app/components/sidebars/index.js @@ -0,0 +1 @@ +export { default } from './sidebar.component' diff --git a/ui/app/components/sidebars/index.scss b/ui/app/components/sidebars/index.scss new file mode 100644 index 000000000..5ab0664df --- /dev/null +++ b/ui/app/components/sidebars/index.scss @@ -0,0 +1,74 @@ +.sidebar-right-enter { + transition: transform 300ms ease-in-out; + transform: translateX(-100%); +} + +.sidebar-right-enter.sidebar-right-enter-active { + transition: transform 300ms ease-in-out; + transform: translateX(0%); +} + +.sidebar-right-leave { + transition: transform 200ms ease-out; + transform: translateX(0%); +} + +.sidebar-right-leave.sidebar-right-leave-active { + transition: transform 200ms ease-out; + transform: translateX(-100%); +} + +.sidebar-left-enter { + transition: transform 300ms ease-in-out; + transform: translateX(100%); +} + +.sidebar-left-enter.sidebar-left-enter-active { + transition: transform 300ms ease-in-out; + transform: translateX(0%); +} + +.sidebar-left-leave { + transition: transform 200ms ease-out; + transform: translateX(0%); +} + +.sidebar-left-leave.sidebar-left-leave-active { + transition: transform 200ms ease-out; + transform: translateX(100%); +} + +.sidebar-left { + flex: 1 0 230px; + background: rgb(250, 250, 250); + z-index: $sidebar-z-index; + position: fixed; + left: 15%; + right: 0; + bottom: 0; + opacity: 1; + visibility: visible; + will-change: transform; + overflow-y: auto; + box-shadow: rgba(0, 0, 0, .15) 2px 2px 4px; + width: 85%; + height: 100%; + + @media screen and (min-width: 769px) { + width: 408px; + left: calc(100% - 408px); + } +} + +.sidebar-overlay { + z-index: $sidebar-overlay-z-index; + position: fixed; + height: 100%; + width: 100%; + left: 0; + right: 0; + bottom: 0; + opacity: 1; + visibility: visible; + background-color: rgba(0, 0, 0, .3); +}
\ No newline at end of file diff --git a/ui/app/components/sidebars/sidebar.component.js b/ui/app/components/sidebars/sidebar.component.js new file mode 100644 index 000000000..57cdd7111 --- /dev/null +++ b/ui/app/components/sidebars/sidebar.component.js @@ -0,0 +1,49 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import ReactCSSTransitionGroup from 'react-addons-css-transition-group' +import WalletView from '../wallet-view' +import { WALLET_VIEW_SIDEBAR } from './sidebar.constants' + +export default class Sidebar extends Component { + + static propTypes = { + sidebarOpen: PropTypes.bool, + hideSidebar: PropTypes.func, + transitionName: PropTypes.string, + type: PropTypes.string, + }; + + renderOverlay () { + return <div className="sidebar-overlay" onClick={() => this.props.hideSidebar()} /> + } + + renderSidebarContent () { + const { type } = this.props + + switch (type) { + case WALLET_VIEW_SIDEBAR: + return <WalletView responsiveDisplayClassname={'sidebar-right' } /> + default: + return null + } + + } + + render () { + const { transitionName, sidebarOpen } = this.props + + return ( + <div> + <ReactCSSTransitionGroup + transitionName={transitionName} + transitionEnterTimeout={300} + transitionLeaveTimeout={200} + > + { sidebarOpen ? this.renderSidebarContent() : null } + </ReactCSSTransitionGroup> + { sidebarOpen ? this.renderOverlay() : null } + </div> + ) + } + +} diff --git a/ui/app/components/sidebars/sidebar.constants.js b/ui/app/components/sidebars/sidebar.constants.js new file mode 100644 index 000000000..1613a8245 --- /dev/null +++ b/ui/app/components/sidebars/sidebar.constants.js @@ -0,0 +1 @@ +export const WALLET_VIEW_SIDEBAR = 'wallet-view' diff --git a/ui/app/components/sidebars/tests/sidebars-component.test.js b/ui/app/components/sidebars/tests/sidebars-component.test.js new file mode 100644 index 000000000..e2d77518a --- /dev/null +++ b/ui/app/components/sidebars/tests/sidebars-component.test.js @@ -0,0 +1,88 @@ +import React from 'react' +import assert from 'assert' +import { shallow } from 'enzyme' +import sinon from 'sinon' +import ReactCSSTransitionGroup from 'react-addons-css-transition-group' +import Sidebar from '../sidebar.component.js' + +import WalletView from '../../wallet-view' + +const propsMethodSpies = { + hideSidebar: sinon.spy(), +} + +describe('Sidebar Component', function () { + let wrapper + + beforeEach(() => { + wrapper = shallow(<Sidebar + sidebarOpen={false} + hideSidebar={propsMethodSpies.hideSidebar} + transitionName={'someTransition'} + type={'wallet-view'} + />) + }) + + afterEach(() => { + propsMethodSpies.hideSidebar.resetHistory() + }) + + describe('renderOverlay', () => { + let renderOverlay + + beforeEach(() => { + renderOverlay = shallow(wrapper.instance().renderOverlay()) + }) + + it('should render a overlay element', () => { + assert(renderOverlay.hasClass('sidebar-overlay')) + }) + + it('should pass the correct onClick function to the element', () => { + assert.equal(propsMethodSpies.hideSidebar.callCount, 0) + renderOverlay.props().onClick() + assert.equal(propsMethodSpies.hideSidebar.callCount, 1) + }) + }) + + describe('renderSidebarContent', () => { + let renderSidebarContent + + beforeEach(() => { + wrapper.setProps({ type: 'wallet-view' }) + renderSidebarContent = wrapper.instance().renderSidebarContent() + }) + + it('should render sidebar content with the correct props', () => { + wrapper.setProps({ type: 'wallet-view' }) + renderSidebarContent = wrapper.instance().renderSidebarContent() + assert.equal(renderSidebarContent.props.responsiveDisplayClassname, 'sidebar-right') + }) + + it('should not render with an unrecognized type', () => { + wrapper.setProps({ type: 'foobar' }) + renderSidebarContent = wrapper.instance().renderSidebarContent() + assert.equal(renderSidebarContent, undefined) + }) + }) + + describe('render', () => { + it('should render a div with one child', () => { + assert(wrapper.is('div')) + assert.equal(wrapper.children().length, 1) + }) + + it('should render the ReactCSSTransitionGroup without any children', () => { + assert(wrapper.children().at(0).is(ReactCSSTransitionGroup)) + assert.equal(wrapper.children().at(0).children().length, 0) + }) + + it('should render sidebar content and the overlay if sidebarOpen is true', () => { + wrapper.setProps({ sidebarOpen: true }) + assert.equal(wrapper.children().length, 2) + assert(wrapper.children().at(1).hasClass('sidebar-overlay')) + assert.equal(wrapper.children().at(0).children().length, 1) + assert(wrapper.children().at(0).children().at(0).is(WalletView)) + }) + }) +}) diff --git a/ui/app/components/token-cell.js b/ui/app/components/token-cell.js index 58a30228d..477d97597 100644 --- a/ui/app/components/token-cell.js +++ b/ui/app/components/token-cell.js @@ -18,7 +18,7 @@ function mapStateToProps (state) { userAddress: selectors.getSelectedAddress(state), contractExchangeRates: state.metamask.contractExchangeRates, conversionRate: state.metamask.conversionRate, - sidebarOpen: state.appState.sidebarOpen, + sidebarOpen: state.appState.sidebar.isOpen, } } diff --git a/ui/app/components/transaction-list-item/transaction-list-item.component.js b/ui/app/components/transaction-list-item/transaction-list-item.component.js index d9e63d6e0..4fddd45ef 100644 --- a/ui/app/components/transaction-list-item/transaction-list-item.component.js +++ b/ui/app/components/transaction-list-item/transaction-list-item.component.js @@ -21,6 +21,7 @@ export default class TransactionListItem extends PureComponent { setSelectedToken: PropTypes.func, nonceAndDate: PropTypes.string, token: PropTypes.object, + assetImages: PropTypes.object, } handleClick = () => { @@ -100,6 +101,7 @@ export default class TransactionListItem extends PureComponent { methodData, showRetry, nonceAndDate, + assetImages, } = this.props const { txParams = {} } = transaction @@ -113,6 +115,7 @@ export default class TransactionListItem extends PureComponent { className="transaction-list-item__identicon" address={txParams.to} diameter={34} + image={assetImages[txParams.to]} /> <TransactionAction transaction={transaction} diff --git a/ui/app/components/transaction-list/transaction-list.component.js b/ui/app/components/transaction-list/transaction-list.component.js index e30476d8c..c864fea3b 100644 --- a/ui/app/components/transaction-list/transaction-list.component.js +++ b/ui/app/components/transaction-list/transaction-list.component.js @@ -21,6 +21,7 @@ export default class TransactionList extends PureComponent { transactionToRetry: PropTypes.object, selectedToken: PropTypes.object, updateNetworkNonce: PropTypes.func, + assetImages: PropTypes.object, } componentDidMount () { @@ -45,7 +46,6 @@ export default class TransactionList extends PureComponent { renderTransactions () { const { t } = this.context const { pendingTransactions = [], completedTransactions = [] } = this.props - return ( <div className="transaction-list__transactions"> { @@ -79,7 +79,7 @@ export default class TransactionList extends PureComponent { } renderTransaction (transaction, index) { - const { selectedToken } = this.props + const { selectedToken, assetImages } = this.props return transaction.key === TRANSACTION_TYPE_SHAPESHIFT ? ( @@ -93,6 +93,7 @@ export default class TransactionList extends PureComponent { key={transaction.id} showRetry={this.shouldShowRetry(transaction)} token={selectedToken} + assetImages={assetImages} /> ) } diff --git a/ui/app/components/transaction-list/transaction-list.container.js b/ui/app/components/transaction-list/transaction-list.container.js index 1ec1f9ccf..2e946c67d 100644 --- a/ui/app/components/transaction-list/transaction-list.container.js +++ b/ui/app/components/transaction-list/transaction-list.container.js @@ -7,7 +7,7 @@ import { submittedPendingTransactionsSelector, completedTransactionsSelector, } from '../../selectors/transactions' -import { getSelectedAddress } from '../../selectors' +import { getSelectedAddress, getAssetImages } from '../../selectors' import { selectedTokenSelector } from '../../selectors/tokens' import { getLatestSubmittedTxWithNonce } from '../../helpers/transactions.util' import { updateNetworkNonce } from '../../actions' @@ -23,6 +23,7 @@ const mapStateToProps = state => { transactionToRetry: getLatestSubmittedTxWithNonce(submittedPendingTransactions, networkNonce), selectedToken: selectedTokenSelector(state), selectedAddress: getSelectedAddress(state), + assetImages: getAssetImages(state), } } diff --git a/ui/app/components/transaction-view-balance/transaction-view-balance.component.js b/ui/app/components/transaction-view-balance/transaction-view-balance.component.js index bdc46f714..1b7a29c87 100644 --- a/ui/app/components/transaction-view-balance/transaction-view-balance.component.js +++ b/ui/app/components/transaction-view-balance/transaction-view-balance.component.js @@ -18,6 +18,7 @@ export default class TransactionViewBalance extends PureComponent { history: PropTypes.object, network: PropTypes.string, balance: PropTypes.string, + assetImage: PropTypes.string, } renderBalance () { @@ -75,7 +76,7 @@ export default class TransactionViewBalance extends PureComponent { } render () { - const { network, selectedToken } = this.props + const { network, selectedToken, assetImage } = this.props return ( <div className="transaction-view-balance"> @@ -84,6 +85,7 @@ export default class TransactionViewBalance extends PureComponent { diameter={50} address={selectedToken && selectedToken.address} network={network} + image={assetImage} /> { this.renderBalance() } </div> diff --git a/ui/app/components/transaction-view-balance/transaction-view-balance.container.js b/ui/app/components/transaction-view-balance/transaction-view-balance.container.js index 1d3432b15..30c5cab16 100644 --- a/ui/app/components/transaction-view-balance/transaction-view-balance.container.js +++ b/ui/app/components/transaction-view-balance/transaction-view-balance.container.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux' import { withRouter } from 'react-router-dom' import { compose } from 'recompose' import TransactionViewBalance from './transaction-view-balance.component' -import { getSelectedToken, getSelectedAddress } from '../../selectors' +import { getSelectedToken, getSelectedAddress, getSelectedTokenAssetImage } from '../../selectors' import { showModal } from '../../actions' const mapStateToProps = state => { @@ -15,6 +15,7 @@ const mapStateToProps = state => { selectedToken: getSelectedToken(state), network, balance, + assetImage: getSelectedTokenAssetImage(state), } } diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js index ffa60e3ed..6de265110 100644 --- a/ui/app/components/wallet-view.js +++ b/ui/app/components/wallet-view.js @@ -34,7 +34,7 @@ function mapStateToProps (state) { return { network: state.metamask.network, - sidebarOpen: state.appState.sidebarOpen, + sidebarOpen: state.appState.sidebar.isOpen, identities: state.metamask.identities, accounts: state.metamask.accounts, tokens: state.metamask.tokens, diff --git a/ui/app/css/itcss/components/newui-sections.scss b/ui/app/css/itcss/components/newui-sections.scss index 29dd18ae3..7eb193d6f 100644 --- a/ui/app/css/itcss/components/newui-sections.scss +++ b/ui/app/css/itcss/components/newui-sections.scss @@ -148,7 +148,7 @@ $wallet-view-bg: $alabaster; } } -.wallet-view.sidebar { +.wallet-view.sidebar-right { flex: 1 0 230px; background: rgb(250, 250, 250); z-index: $sidebar-z-index; @@ -166,20 +166,6 @@ $wallet-view-bg: $alabaster; height: calc(100% - 56px); } -.sidebar-overlay { - z-index: $sidebar-overlay-z-index; - position: fixed; - // top: 41px; - height: 100%; - width: 100%; - left: 0; - right: 0; - bottom: 0; - opacity: 1; - visibility: visible; - background-color: rgba(0, 0, 0, .3); -} - // main-container media queries @media screen and (min-width: 576px) { diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index 7be9b8d40..5c86d397d 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -48,7 +48,11 @@ function reduceApp (state, action) { name: null, }, }, - sidebarOpen: false, + sidebar: { + isOpen: false, + transitionName: '', + type: '', + }, alertOpen: false, alertMessage: null, qrCodeData: null, @@ -88,12 +92,18 @@ function reduceApp (state, action) { // sidebar methods case actions.SIDEBAR_OPEN: return extend(appState, { - sidebarOpen: true, + sidebar: { + ...action.value, + isOpen: true, + }, }) case actions.SIDEBAR_CLOSE: return extend(appState, { - sidebarOpen: false, + sidebar: { + ...appState.sidebar, + isOpen: false, + }, }) // alert methods diff --git a/ui/app/selectors.js b/ui/app/selectors.js index 1d5f4d4cb..fb4517628 100644 --- a/ui/app/selectors.js +++ b/ui/app/selectors.js @@ -14,6 +14,8 @@ const selectors = { getSelectedAccount, getSelectedToken, getSelectedTokenExchangeRate, + getSelectedTokenAssetImage, + getAssetImages, getTokenExchangeRate, conversionRateSelector, transactionsSelector, @@ -71,6 +73,18 @@ function getSelectedTokenExchangeRate (state) { return contractExchangeRates[address] || 0 } +function getSelectedTokenAssetImage (state) { + const assetImages = state.metamask.assetImages || {} + const selectedToken = getSelectedToken(state) || {} + const { address } = selectedToken + return assetImages[address] +} + +function getAssetImages (state) { + const assetImages = state.metamask.assetImages || {} + return assetImages +} + function getTokenExchangeRate (state, address) { const contractExchangeRates = state.metamask.contractExchangeRates return contractExchangeRates[address] || 0 |