diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 18127ec5..00000000 --- a/.babelrc +++ /dev/null @@ -1,31 +0,0 @@ -{ - "presets": [ - ["env", { - "useBuiltIns": true, - "loose": true - }], - "react" - ], - "plugins": [ - "react-hot-loader/babel", - "transform-class-properties", - "transform-object-rest-spread", - "transform-flow-strip-types", - ["transform-runtime", { - "polyfill": false, - "regenerator": true - }], - ["module-resolver", { - "root": ["./src"], - "alias": { - "public": ["./public"] - } - }], - "babel-plugin-styled-components" - ], - "env": { - "test": { - "presets": ["jest"] - } - } -} \ No newline at end of file diff --git a/.eslintrc b/.eslintrc index a2540fde..a825aa3a 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,7 +10,8 @@ }, "env": { "browser": true, - "jest": true + "jest": true, + "cypress/globals": true }, "rules": { "import/prefer-default-export": 0, @@ -32,13 +33,17 @@ "new-cap": 0, "max-len": 0, "eol-last": 0, - "spaced-comment": 0 + "spaced-comment": 0, + "no-unused-expressions": 0, + "chai-friendly/no-unused-expressions": 2 }, "plugins": [ "import", "react", "jest", - "flowtype" + "flowtype", + "cypress", + "chai-friendly" ], "settings": { "import/resolver": { @@ -52,8 +57,7 @@ "ecmaVersion": 7, "sourceType": "module", "ecmaFeatures": { - "jsx": true, - "experimentalObjectRestSpread": true + "jsx": true } } } \ No newline at end of file diff --git a/.flowconfig b/.flowconfig index 97214ee1..13295242 100644 --- a/.flowconfig +++ b/.flowconfig @@ -10,6 +10,7 @@ .*/_old/.* .*/scripts/solidity/.* .*/build/.* +.*/cache/.* [libs] ./src/flowtype/npm/redux_v3.x.x.js diff --git a/.gitignore b/.gitignore index 495fb80e..9120523b 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,6 @@ logs _old -coverage \ No newline at end of file +coverage +test/**/__diff_output__ +test/screenshots \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f0a4b250..3046d582 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,14 +1,19 @@ -image: node:9.3 +image: node:10.15.1 + +variables: + CYPRESS_CACHE_FOLDER: "$CI_PROJECT_DIR/cache/Cypress" cache: key: ${CI_COMMIT_REF_SLUG} paths: - - node_modules/ + - node_modules + - ${CYPRESS_CACHE_FOLDER} stages: - test - build - deploy + - integration tests lint: stage: test @@ -62,7 +67,23 @@ build stable: expire_in: 1 week paths: - build/stable - - scripts/s3sync.sh + - scripts/s3sync.sh + +build emulator and bridge image: + variables: + CONTAINER_NAME: "$CI_REGISTRY/emulator-bridge-tests" + image: docker:latest + services: + - docker:dind + before_script: + - docker login $CI_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD + stage: build + when: manual + script: + - docker pull $CONTAINER_NAME:latest || true + - docker build --cache-from $CONTAINER_NAME:latest --tag $CONTAINER_NAME:$CI_COMMIT_SHA --tag $CONTAINER_NAME:latest . + - docker push $CONTAINER_NAME:$CI_COMMIT_SHA + - docker push $CONTAINER_NAME:latest deploy review: stage: deploy @@ -138,3 +159,24 @@ delete review: - branches tags: - deploy + +integration tests: + image: docker:latest + services: + - docker:dind + stage: integration tests + script: + - 'export SHARED_PATH="$(dirname ${CI_PROJECT_DIR})/shared"' + - rm -r ${SHARED_PATH} || true + - docker build -t wallet-emulator-bridge-tests . + - mkdir -p ${SHARED_PATH}/trezor-wallet/screenshots + - mkdir -p ${SHARED_PATH}/trezor-wallet/videos + - docker run --volume ${SHARED_PATH}/trezor-wallet/screenshots:/trezor-wallet/test/screenshots --volume ${SHARED_PATH}/trezor-wallet/videos:/trezor-wallet/test/videos --rm wallet-emulator-bridge-tests + - find ${SHARED_PATH} + - mkdir trezor-wallet + - cp -r ${SHARED_PATH}/ trezor-wallet/ + artifacts: + when: always + expire_in: 1 week + paths: + - trezor-wallet/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 7d2db3a6..06d122b5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,50 @@ -FROM node:9.3 +FROM python:latest -ARG BUILD_TYPE=stable +# +# setup +# +RUN apt-get update +RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - +RUN apt-get install -y chromium libappindicator3-1 xdg-utils fonts-liberation nodejs wget dpkg git python python3 python3-pip xvfb libgtk2.0-0 libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 +RUN npm install -g yarn -WORKDIR /trezor-wallet-app +RUN ln -s /usr/bin/chromium /usr/local/bin/chromium-browser -COPY package.json /trezor-wallet-app -COPY yarn.lock /trezor-wallet-app +# +# build emulator +# +RUN mkdir /trezor-emulator +WORKDIR /trezor-emulator -RUN yarn install +RUN git clone https://github.com/trezor/trezor-core +WORKDIR /trezor-emulator/trezor-core +RUN git submodule update --init --recursive -COPY . /trezor-wallet-app +RUN apt-get install libusb-1.0-0 +RUN pip3 install scons trezor +RUN make build_unix_noui -RUN yarn run build:${BUILD_TYPE} +# +# install bridge +# +RUN mkdir /trezor-bridge +WORKDIR /trezor-bridge +RUN wget https://wallet.trezor.io/data/bridge/2.0.25/trezor-bridge_2.0.25_amd64.deb +RUN dpkg -x /trezor-bridge/trezor-bridge_2.0.25_amd64.deb /trezor-bridge/extracted -EXPOSE 8080 -CMD [ "yarn", "run", "prod-server" ] \ No newline at end of file +# +# install trezor-wallet +# +RUN mkdir /trezor-wallet +WORKDIR /trezor-wallet +COPY package.json /trezor-wallet +COPY yarn.lock /trezor-wallet +RUN yarn +COPY . /trezor-wallet +RUN yarn run build:stable + +# +# run +# +ENTRYPOINT ["/trezor-wallet/test/scripts/run-all.sh"] +EXPOSE 8080 21325 \ No newline at end of file diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 00000000..63b60eee --- /dev/null +++ b/babel.config.js @@ -0,0 +1,51 @@ +module.exports = (api) => { + // api.cache.forever(); + + const presets = [ + [ + '@babel/preset-env', + { + useBuiltIns: 'entry', + loose: true, + }, + ], + '@babel/preset-react', + '@babel/preset-flow', + ]; + + const plugins = [ + 'react-hot-loader/babel', + '@babel/plugin-transform-flow-strip-types', + '@babel/plugin-proposal-class-properties', + '@babel/plugin-proposal-object-rest-spread', + [ + '@babel/plugin-transform-runtime', + { + regenerator: true, + }, + ], + [ + 'module-resolver', + { + root: [ + './src', + ], + alias: { + public: [ + './public', + ], + }, + }, + ], + 'babel-plugin-styled-components', + ]; + + if (api.env('test')) { + presets.push('jest'); + } + + return { + presets, + plugins, + }; +}; \ No newline at end of file diff --git a/cypress.json b/cypress.json new file mode 100644 index 00000000..f7910b54 --- /dev/null +++ b/cypress.json @@ -0,0 +1,11 @@ +{ + "integrationFolder": "test/integration", + "fixturesFolder": "test/fixtures", + "pluginsFile": "test/plugins/index.js", + "supportFile": "test/support/index.js", + "defaultCommandTimeout": 10000, + "screenshotsFolder": "test/screenshots", + "video": false, + "trashAssetsBeforeRuns": true, + "chromeWebSecurity": false +} \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 10729508..36b82bc6 100644 --- a/jest.config.js +++ b/jest.config.js @@ -17,4 +17,7 @@ module.exports = { setupFiles: [ './support/setupJest.js', ], + transform: { + '^.+\\.jsx?$': 'babel-jest', + }, }; diff --git a/package.json b/package.json index 111865b9..b88487f5 100644 --- a/package.json +++ b/package.json @@ -21,24 +21,25 @@ "test": "run-s test:*", "test:unit": "npx jest", "test-unit:watch": "npx jest -o --watch", + "test-integration:dev": "npx cypress open -c baseUrl=http://localhost:8081/#/", + "test-integration:test": "npx cypress run", + "test-integration:gitlab": "npx cypress run -c baseUrl=https://localhost:8080/#/ --browser chromium", "server:beta": "node ./server/index.js --buildType=beta", "server:stable": "node ./server/index.js --buildType=stable" }, "dependencies": { - "babel": "^6.23.0", - "babel-core": "^6.26.3", + "@babel/polyfill": "^7.2.5", "bignumber.js": "2.4.0", "color-hash": "^1.0.3", "commander": "^2.19.0", - "connected-react-router": "^6.0.0", - "copy-webpack-plugin": "^4.5.2", + "connected-react-router": "^6.2.2", + "copy-webpack-plugin": "^4.6.0", "cross-env": "^5.2.0", - "date-fns": "^1.29.0", - "ethereumjs-tx": "^1.3.3", + "date-fns": "^1.30.1", + "ethereumjs-tx": "^1.3.7", "ethereumjs-units": "^0.2.0", - "ethereumjs-util": "^5.1.4", - "express": "^4.16.3", - "flow-webpack-plugin": "^1.2.0", + "ethereumjs-util": "^5.2.0", + "express": "^4.16.4", "friendly-errors-webpack-plugin": "^1.7.0", "git-revision-webpack-plugin": "^3.0.3", "hdkey": "^0.8.0", @@ -46,78 +47,87 @@ "html-webpack-plugin": "^3.2.0", "jest-fetch-mock": "^1.6.5", "morgan": "^1.9.1", - "npm-run-all": "^4.1.3", + "npm-run-all": "^4.1.5", "prop-types": "^15.6.2", - "raf": "^3.4.0", - "raven-js": "^3.22.3", - "rc-tooltip": "^3.7.0", - "react": "^16.6.3", - "react-dom": "^16.6.3", - "react-hot-loader": "^4.6.2", "react-intl": "^2.8.0", + "raf": "^3.4.1", + "raven-js": "^3.27.0", + "rc-tooltip": "^3.7.3", + "react": "^16.7.0", + "react-dom": "^16.7.0", + "react-hot-loader": "^4.6.5", "react-json-view": "^1.19.1", "react-qr-reader": "^2.1.2", "react-qr-svg": "^2.1.0", "react-redux": "^6.0.0", "react-router": "^4.3.1", - "react-router-dom": "^4.2.2", + "react-router-dom": "^4.3.1", "react-scale-text": "^1.2.2", - "react-select": "^2.2.0", - "react-textarea-autosize": "^7.0.4", - "react-transition-group": "^2.4.0", + "react-select": "^2.3.0", + "react-textarea-autosize": "^7.1.0", + "react-transition-group": "^2.5.3", "redbox-react": "^1.6.0", - "redux": "4.0.0", + "redux": "4.0.1", "redux-logger": "^3.0.6", "redux-raven-middleware": "^1.2.0", - "redux-thunk": "^2.2.0", - "rimraf": "^2.6.2", - "styled-components": "^4.1.2", - "styled-normalize": "^8.0.4", + "redux-thunk": "^2.3.0", + "request": "^2.88.0", + "rimraf": "^2.6.3", + "styled-components": "^4.1.3", + "styled-normalize": "^8.0.6", + "trezor-bridge-communicator": "1.0.2", "trezor-connect": "7.0.0-beta.2", "wallet-address-validator": "^0.2.4", "web3": "1.0.0-beta.35", - "webpack": "^4.16.3", - "webpack-build-notifier": "^0.1.29", - "webpack-bundle-analyzer": "^2.13.1", + "webpack": "^4.29.1", + "webpack-build-notifier": "^0.1.30", + "webpack-bundle-analyzer": "^3.0.3", "whatwg-fetch": "^2.0.4", "yarn-run-all": "^3.1.1" }, "devDependencies": { - "babel-cli": "^6.24.1", - "babel-eslint": "^8.2.6", - "babel-loader": "^7.1.5", - "babel-plugin-module-resolver": "^3.1.1", - "babel-plugin-styled-components": "^1.5.1", - "babel-plugin-transform-class-properties": "^6.24.1", - "babel-plugin-transform-flow-strip-types": "^6.22.0", - "babel-plugin-transform-object-rest-spread": "^6.23.0", - "babel-plugin-transform-runtime": "^6.23.0", - "babel-preset-env": "^1.6.0", - "babel-preset-jest": "^23.2.0", - "babel-preset-react": "^6.24.1", - "eslint": "^4", - "eslint-config-airbnb": "^17.0.0", - "eslint-import-resolver-babel-module": "^4.0.0", - "eslint-loader": "^2.1.0", - "eslint-plugin-flowtype": "^2.50.0", - "eslint-plugin-import": "^2.14.0", - "eslint-plugin-jest": "^21.18.0", - "eslint-plugin-jsx-a11y": "^6.1.1", - "eslint-plugin-react": "^7.10.0", - "file-loader": "1.1.11", + "@babel/cli": "^7.2.3", + "@babel/core": "^7.2.2", + "@babel/plugin-proposal-class-properties": "^7.3.0", + "@babel/plugin-proposal-object-rest-spread": "^7.3.2", + "@babel/plugin-transform-flow-strip-types": "^7.2.3", + "@babel/plugin-transform-runtime": "^7.2.0", + "@babel/preset-env": "^7.3.1", + "@babel/preset-flow": "^7.0.0", + "@babel/preset-react": "^7.0.0", + "@babel/register": "^7.0.0", + "babel-eslint": "^10.0.1", + "babel-jest": "^24.1.0", + "babel-loader": "^8.0.5", + "babel-plugin-module-resolver": "^3.1.3", + "babel-plugin-styled-components": "^1.10.0", + "cypress": "^3.1.5", + "cypress-image-snapshot": "^3.0.0", + "eslint": "^5.13.0", + "eslint-config-airbnb": "^17.1.0", + "eslint-import-resolver-babel-module": "^5.0.1", + "eslint-loader": "^2.1.2", + "eslint-plugin-chai-friendly": "^0.4.1", + "eslint-plugin-cypress": "^2.2.0", + "eslint-plugin-flowtype": "^3.2.1", + "eslint-plugin-import": "^2.16.0", + "eslint-plugin-jest": "^22.2.2", + "eslint-plugin-jsx-a11y": "^6.2.1", + "eslint-plugin-react": "^7.12.4", + "file-loader": "3.0.1", "flow-bin": "0.75.0", - "jest": "^23.4.2", - "stylelint": "^8.0.0", + "jest": "^24.1.0", + "stylelint": "^9.10.1", "stylelint-config-standard": "^18.2.0", "stylelint-config-styled-components": "^0.1.1", - "stylelint-custom-processor-loader": "^0.5.0", - "stylelint-processor-styled-components": "^1.3.2", + "stylelint-custom-processor-loader": "^0.6.0", + "stylelint-processor-styled-components": "^1.5.2", "stylelint-webpack-plugin": "^0.10.5", - "webpack-cli": "^2.1.3", - "webpack-dev-server": "^3.1.4", + "webpack-cli": "^3.2.3", + "webpack-dev-server": "^3.1.14", "yargs": "11.0.0" }, "optionalDependencies": { - "fsevents": "*" + "fsevents": "1.2.7" } } diff --git a/src/components/Button/index.js b/src/components/Button/index.js index a5f3931d..65f56aa9 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -17,6 +17,7 @@ type Props = { isWhite?: boolean, isWebUsb?: boolean, isTransparent?: boolean, + dataTest?: string } const Wrapper = styled.button` @@ -146,10 +147,12 @@ const Button = ({ isWhite = false, isWebUsb = false, isTransparent = false, + dataTest, }: Props) => { const newClassName = isWebUsb ? `${className} trezor-webusb-button` : className; return ( { const status = getStatus(device); return ( ( - + { sidebarEnabled && {sidebarOpened ? '✕ Close' : '☰ Menu'}} diff --git a/src/components/Tooltip/index.js b/src/components/Tooltip/index.js index 32f504b3..9bd4ec68 100644 --- a/src/components/Tooltip/index.js +++ b/src/components/Tooltip/index.js @@ -31,11 +31,13 @@ const Tooltip = ({ content, readMoreLink, children, + enterDelayMs, }) => ( } placement={placement} + mouseEnterDelay={enterDelayMs || 0} overlay={() => ( {content} @@ -45,7 +47,8 @@ const Tooltip = ({ ) } - )} + + )} > {children} @@ -65,6 +68,7 @@ Tooltip.propTypes = { PropTypes.string, ]), readMoreLink: PropTypes.string, + enterDelayMs: PropTypes.bool, }; export default Tooltip; diff --git a/src/components/images/WalletType/index.js b/src/components/images/WalletType/index.js index 81eeacfb..867243cb 100644 --- a/src/components/images/WalletType/index.js +++ b/src/components/images/WalletType/index.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import colors from 'config/colors'; import styled from 'styled-components'; +import ICONS from 'config/icons'; const SvgWrapper = styled.svg` :hover { @@ -15,9 +16,6 @@ const Path = styled.path` fill: ${props => props.color}; `; -export const HIDDEN = 'M25.421,17.28l-3.167-8.8C22.175,8.24,21.858,8,21.542,8h-2.375c-0.475,0-0.792,0.32-0.792,0.8c0,0.48,0.317,0.8,0.792,0.8h1.821l2.612,7.2h-6.017h-3.167H8.4l2.613-7.2h1.821c0.475,0,0.792-0.32,0.792-0.8c0-0.48-0.317-0.8-0.792-0.8h-2.375c-0.317,0-0.633,0.24-0.712,0.56l-3.167,8.8C6.5,17.36,6.5,17.52,6.5,17.6l0,0l0,0l0,0v4c0,1.36,1.029,2.4,2.375,2.4h3.958c1.346,0,2.375-1.04,2.375-2.4v-3.2h1.583v3.2c0,1.36,1.029,2.4,2.375,2.4h3.958c1.346,0,2.375-1.04,2.375-2.4v-4l0,0l0,0l0,0C25.5,17.52,25.5,17.36,25.421,17.28z'; -export const STANDARD = 'M23.333,10.667H10.667H10c-0.367,0-0.667-0.299-0.667-0.667S9.633,9.333,10,9.333h10V10h1.333V8.667C21.333,8.299,21.035,8,20.667,8H10c-1.105,0-2,0.895-2,2v11.333C8,22.806,9.194,24,10.667,24h12.667C23.701,24,24,23.701,24,23.333v-12C24,10.965,23.701,10.667,23.333,10.667z M20,18.667c-0.737,0-1.333-0.597-1.333-1.333C18.667,16.597,19.263,16,20,16s1.333,0.597,1.333,1.333C21.333,18.07,20.737,18.667,20,18.667z'; - const Icon = ({ type = 'standard', size = 24, @@ -29,13 +27,13 @@ const Icon = ({ hoverColor={hoverColor} width={`${size}`} height={`${size}`} - viewBox="0 0 32 32" + viewBox="0 0 1024 1024" onClick={onClick} > ); diff --git a/src/views/Wallet/components/LeftNavigation/components/CoinMenu/index.js b/src/views/Wallet/components/LeftNavigation/components/CoinMenu/index.js index 6d262662..194932e7 100644 --- a/src/views/Wallet/components/LeftNavigation/components/CoinMenu/index.js +++ b/src/views/Wallet/components/LeftNavigation/components/CoinMenu/index.js @@ -57,7 +57,7 @@ class CoinMenu extends PureComponent { render() { const { config } = this.props.localStorage; return ( - + {config.networks.map(item => ( { ))} - {/* this.onDeviceMenuClick('settings', this.props.device)}> + {/* { + this.props.toggleDeviceDropdown(false); + this.props.gotoDeviceSettings(device); + }} + > */} - this.onDeviceMenuClick('forget', this.props.device)}> - - - {this.showClone() && ( - this.onDeviceMenuClick('clone', this.props.device)}> - + this.props.duplicateDevice(device)}> + )} {this.showRenewSession() && ( this.onDeviceMenuClick('reload')} + onClick={() => this.props.acquireDevice()} > )} + this.props.forgetDevice(device)}> + + + ); } @@ -100,8 +96,8 @@ MenuItems.propTypes = { acquireDevice: PropTypes.func.isRequired, forgetDevice: PropTypes.func.isRequired, duplicateDevice: PropTypes.func.isRequired, - toggleDeviceDropdown: PropTypes.func.isRequired, - gotoDeviceSettings: PropTypes.func.isRequired, + // toggleDeviceDropdown: PropTypes.func.isRequired, + // gotoDeviceSettings: PropTypes.func.isRequired, }; export default MenuItems; \ No newline at end of file diff --git a/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/index.js b/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/index.js index 0929ff93..0d0b4cc5 100644 --- a/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/index.js +++ b/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/index.js @@ -2,7 +2,6 @@ import React, { PureComponent } from 'react'; import styled from 'styled-components'; import TrezorConnect from 'trezor-connect'; -import type { TrezorDevice } from 'flowtype'; import COLORS from 'config/colors'; import { FONT_SIZE, FONT_WEIGHT } from 'config/variables'; @@ -45,11 +44,6 @@ const StyledDivider = styled(Divider)` border: none; `; -type DeviceMenuItem = { - type: string; - label: string; -} - class DeviceMenu extends PureComponent { constructor(props: Props) { super(props); @@ -74,19 +68,6 @@ class DeviceMenu extends PureComponent { window.removeEventListener('mousedown', this.mouseDownHandler, false); } - onDeviceMenuClick(item: DeviceMenuItem, device: TrezorDevice): void { - if (item.type === 'reload') { - this.props.acquireDevice(); - } else if (item.type === 'forget') { - this.props.forgetDevice(device); - } else if (item.type === 'clone') { - this.props.duplicateDevice(device); - } else if (item.type === 'settings') { - this.props.toggleDeviceDropdown(false); - this.props.gotoDeviceSettings(device); - } - } - getMenuHeight(): number { return this.myRef.current ? this.myRef.current.getBoundingClientRect().height : 0; } diff --git a/src/views/Wallet/components/LeftNavigation/components/Divider/index.js b/src/views/Wallet/components/LeftNavigation/components/Divider/index.js index 1cce3321..b848e4bd 100644 --- a/src/views/Wallet/components/LeftNavigation/components/Divider/index.js +++ b/src/views/Wallet/components/LeftNavigation/components/Divider/index.js @@ -23,9 +23,10 @@ const TextLeft = styled.p` `; const Divider = ({ - textLeft, textRight, hasBorder = false, className, + textLeft, textRight, hasBorder = false, className, testId, }) => ( @@ -39,6 +40,7 @@ Divider.propTypes = { textLeft: PropTypes.string, textRight: PropTypes.string, hasBorder: PropTypes.bool, + testId: PropTypes.string, }; export default Divider; diff --git a/src/views/Wallet/components/LeftNavigation/index.js b/src/views/Wallet/components/LeftNavigation/index.js index 8e926f67..ac7a37ab 100644 --- a/src/views/Wallet/components/LeftNavigation/index.js +++ b/src/views/Wallet/components/LeftNavigation/index.js @@ -12,6 +12,7 @@ import styled from 'styled-components'; import DeviceHeader from 'components/DeviceHeader'; import * as deviceUtils from 'utils/device'; +import Tooltip from 'components/Tooltip'; import AccountMenu from './components/AccountMenu'; import CoinMenu from './components/CoinMenu'; import DeviceMenu from './components/DeviceMenu'; @@ -24,6 +25,9 @@ const Header = styled(DeviceHeader)` flex: 0 0 auto; `; +const WalletTypeIconWrapper = styled.div` +`; + const Counter = styled.div` display: flex; justify-content: center; @@ -203,10 +207,24 @@ class LeftNavigation extends React.PureComponent { const { selectedDevice, dropdownOpened } = props.wallet; const isDeviceAccessible = deviceUtils.isDeviceAccessible(selectedDevice); + const walletType = selectedDevice && !selectedDevice.useEmptyPassphrase ? 'hidden' : 'standard'; + const showWalletType = selectedDevice && selectedDevice.features && selectedDevice.features.passphrase_protection; + const isDeviceReady = selectedDevice && selectedDevice.connected && selectedDevice.available; + + let walletTooltipMsg = `You are in your ${walletType} wallet.`; + if (isDeviceReady) { + walletTooltipMsg = walletType === 'standard' + ? `${walletTooltipMsg} Click here to access your hidden wallet.` + : `${walletTooltipMsg} Click here to access your standard or another hidden wallet`; + } else { + walletTooltipMsg = `${walletTooltipMsg} To access other wallets please connect your device.`; + } + return (
{ if (isDeviceAccessible || this.props.devices.length > 1) { @@ -218,9 +236,38 @@ class LeftNavigation extends React.PureComponent { isOpen={this.props.wallet.dropdownOpened} icon={( - + {showWalletType ? ( + + + { + if (selectedDevice && isDeviceReady) { + this.props.duplicateDevice(selectedDevice); + e.stopPropagation(); + } + }} + hoverColor={isDeviceReady ? colors.TEXT_PRIMARY : colors.TEXT_SECONDARY} + type={walletType} + size={25} + color={colors.TEXT_SECONDARY} + /> + + ) : null + } {this.props.devices.length > 1 && ( - {this.props.devices.length} + + {this.props.devices.length} + )} { {dropdownOpened && } {isDeviceAccessible && menu} -