mirror of
https://github.com/trezor/trezor-wallet
synced 2025-02-05 12:51:44 +00:00
merge
This commit is contained in:
commit
434c86dc8c
@ -3,4 +3,5 @@ build
|
|||||||
coverage
|
coverage
|
||||||
node_modules
|
node_modules
|
||||||
src/flowtype/npm
|
src/flowtype/npm
|
||||||
|
scripts/solidity/.*
|
||||||
**/_old/*
|
**/_old/*
|
@ -9,7 +9,7 @@
|
|||||||
.*/node_modules/react-router-redux/.*
|
.*/node_modules/react-router-redux/.*
|
||||||
.*/node_modules/oboe/test/.*
|
.*/node_modules/oboe/test/.*
|
||||||
.*/_old/.*
|
.*/_old/.*
|
||||||
.*/public/solidity/.*
|
.*/scripts/solidity/.*
|
||||||
.*/build/.*
|
.*/build/.*
|
||||||
|
|
||||||
[libs]
|
[libs]
|
||||||
|
107
.gitlab-ci.yml
107
.gitlab-ci.yml
@ -1,20 +1,109 @@
|
|||||||
image: node:8
|
image: node:8
|
||||||
|
|
||||||
before_script:
|
|
||||||
- yarn install
|
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
paths:
|
paths:
|
||||||
- node_modules/
|
- node_modules
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- test
|
||||||
|
- build
|
||||||
|
- deploy
|
||||||
|
|
||||||
|
lint:
|
||||||
|
stage: test
|
||||||
|
script:
|
||||||
|
- yarn install
|
||||||
|
- yarn run lint
|
||||||
|
|
||||||
flow:
|
flow:
|
||||||
|
stage: test
|
||||||
script:
|
script:
|
||||||
- yarn run flow
|
- yarn run flow
|
||||||
|
|
||||||
test:lint:
|
unit:
|
||||||
script:
|
stage: test
|
||||||
- yarn run lint
|
|
||||||
|
|
||||||
test:unit:
|
|
||||||
script:
|
script:
|
||||||
|
- yarn install
|
||||||
- yarn run test
|
- yarn run test
|
||||||
|
|
||||||
|
build development:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- yarn install
|
||||||
|
- yarn run build:dev
|
||||||
|
artifacts:
|
||||||
|
expire_in: 1 week
|
||||||
|
paths:
|
||||||
|
- build/dev
|
||||||
|
|
||||||
|
build beta:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- yarn install
|
||||||
|
- yarn run build:beta
|
||||||
|
artifacts:
|
||||||
|
expire_in: 1 week
|
||||||
|
paths:
|
||||||
|
- build/beta
|
||||||
|
- scripts/s3sync.sh
|
||||||
|
|
||||||
|
build stable:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- yarn install
|
||||||
|
- yarn run build:stable
|
||||||
|
artifacts:
|
||||||
|
expire_in: 1 week
|
||||||
|
paths:
|
||||||
|
- build/stable
|
||||||
|
- scripts/s3sync.sh
|
||||||
|
|
||||||
|
deploy review:
|
||||||
|
stage: deploy
|
||||||
|
variables:
|
||||||
|
GIT_STRATEGY: none
|
||||||
|
dependencies:
|
||||||
|
- build development
|
||||||
|
script:
|
||||||
|
- echo "Deploy a review app"
|
||||||
|
- '[ -z "${DEPLOY_BASE_DIR}" ] && echo "Deploy base dir cannot be empty" && exit 255'
|
||||||
|
- env
|
||||||
|
- mkdir -p "${DEPLOY_BASE_DIR}/${CI_BUILD_REF_NAME}"
|
||||||
|
- echo "Copy dev build to web server ${DEPLOY_BASE_DIR}/${CI_BUILD_REF_NAME}..."
|
||||||
|
- rsync --delete -va build/dev/ "${DEPLOY_BASE_DIR}/${CI_BUILD_REF_NAME}/"
|
||||||
|
only:
|
||||||
|
- branches
|
||||||
|
tags:
|
||||||
|
- deploy
|
||||||
|
|
||||||
|
deploy stage beta:
|
||||||
|
stage: deploy
|
||||||
|
variables:
|
||||||
|
GIT_STRATEGY: none
|
||||||
|
AWS_ACCESS_KEY_ID: $STAGE_AWS_ACCESS_KEY_ID
|
||||||
|
AWS_SECRET_ACCESS_KEY: $STAGE_AWS_SECRET_ACCESS_KEY
|
||||||
|
when: manual
|
||||||
|
dependencies:
|
||||||
|
- build beta
|
||||||
|
script:
|
||||||
|
- scripts/s3sync.sh stage beta
|
||||||
|
only:
|
||||||
|
- beta
|
||||||
|
tags:
|
||||||
|
- deploy
|
||||||
|
|
||||||
|
deploy stage stable:
|
||||||
|
stage: deploy
|
||||||
|
variables:
|
||||||
|
GIT_STRATEGY: none
|
||||||
|
AWS_ACCESS_KEY_ID: $STAGE_AWS_ACCESS_KEY_ID
|
||||||
|
AWS_SECRET_ACCESS_KEY: $STAGE_AWS_SECRET_ACCESS_KEY
|
||||||
|
when: manual
|
||||||
|
dependencies:
|
||||||
|
- build stable
|
||||||
|
script:
|
||||||
|
- scripts/s3sync.sh stage stable
|
||||||
|
only:
|
||||||
|
- stable
|
||||||
|
tags:
|
||||||
|
- deploy
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
FROM node:8
|
FROM node:9.3
|
||||||
|
|
||||||
|
ARG BUILD_TYPE=stable
|
||||||
|
|
||||||
WORKDIR /trezor-wallet-app
|
WORKDIR /trezor-wallet-app
|
||||||
|
|
||||||
@ -9,7 +11,7 @@ RUN yarn install
|
|||||||
|
|
||||||
COPY . /trezor-wallet-app
|
COPY . /trezor-wallet-app
|
||||||
|
|
||||||
RUN yarn run build:prod
|
RUN yarn run build:${BUILD_TYPE}
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
CMD [ "yarn", "run", "prod-server" ]
|
CMD [ "yarn", "run", "prod-server" ]
|
28
Makefile
Normal file
28
Makefile
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Run docker build
|
||||||
|
# usage:
|
||||||
|
# make build-beta
|
||||||
|
# make build-stable
|
||||||
|
build-%:
|
||||||
|
./scripts/docker-build.sh $*
|
||||||
|
|
||||||
|
# s3sync with stage.mytrezor.com
|
||||||
|
# Upload requested build for testing purposes
|
||||||
|
# usage:
|
||||||
|
# make stage-beta
|
||||||
|
# make stage-stable
|
||||||
|
stage-%:
|
||||||
|
./scripts/s3sync.sh stage $*
|
||||||
|
|
||||||
|
# s3sync with beta.mytrezor.com
|
||||||
|
# Upload build/beta only
|
||||||
|
# usage:
|
||||||
|
# make beta
|
||||||
|
beta:
|
||||||
|
./scripts/s3sync.sh beta beta
|
||||||
|
|
||||||
|
# s3sync with wallet.mytrezor.com
|
||||||
|
# Upload build/stable only
|
||||||
|
# usage:
|
||||||
|
# make stable
|
||||||
|
stable:
|
||||||
|
./scripts/s3sync.sh stable stable
|
@ -1,4 +1,4 @@
|
|||||||
# Trezor Ethereum Wallet
|
# Trezor Wallet
|
||||||
|
|
||||||
To install dependencies run `npm install` or `yarn`
|
To install dependencies run `npm install` or `yarn`
|
||||||
To start locally run `npm run dev` or `yarn run dev`
|
To start locally run `npm run dev` or `yarn run dev`
|
||||||
|
10
package.json
10
package.json
@ -6,12 +6,12 @@
|
|||||||
"bin": {
|
"bin": {
|
||||||
"flow": "./node_modules/flow-bin"
|
"flow": "./node_modules/flow-bin"
|
||||||
},
|
},
|
||||||
"license": "T-RSL",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "npx webpack-dev-server --config webpack/dev.babel.js",
|
"dev": "npx webpack-dev-server --config webpack/dev.babel.js",
|
||||||
"dev:local": "npx webpack-dev-server --config webpack/local.babel.js",
|
"dev:local": "npx webpack-dev-server --config webpack/local.babel.js",
|
||||||
"build": "rimraf build && run-s build:*",
|
"build": "rimraf build && run-s build:*",
|
||||||
"build:prod": "rimraf build/prod && npx webpack --config webpack/production.babel.js --output-path build/prod --progress --bail",
|
"build:stable": "rimraf build/stable && npx webpack --config webpack/production.babel.js --output-path build/stable --progress --bail",
|
||||||
"build:beta": "rimraf build/beta && cross-env BUILD=beta npx webpack --config webpack/production.babel.js --output-path build/beta --progress --bail",
|
"build:beta": "rimraf build/beta && cross-env BUILD=beta npx webpack --config webpack/production.babel.js --output-path build/beta --progress --bail",
|
||||||
"build:dev": "rimraf build.dev && cross-env BUILD=development npx webpack --config webpack/production.babel.js --output-path build/dev --progress --bail",
|
"build:dev": "rimraf build.dev && cross-env BUILD=development npx webpack --config webpack/production.babel.js --output-path build/dev --progress --bail",
|
||||||
"flow": "flow check src",
|
"flow": "flow check src",
|
||||||
@ -21,13 +21,15 @@
|
|||||||
"test": "run-s test:*",
|
"test": "run-s test:*",
|
||||||
"test:unit": "npx jest",
|
"test:unit": "npx jest",
|
||||||
"test-unit:watch": "npx jest -o --watch",
|
"test-unit:watch": "npx jest -o --watch",
|
||||||
"prod-server": "node ./server/index.js"
|
"server:beta": "node ./server/index.js --buildType=beta",
|
||||||
|
"server:stable": "node ./server/index.js --buildType=stable"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel": "^6.23.0",
|
"babel": "^6.23.0",
|
||||||
"babel-core": "^6.26.3",
|
"babel-core": "^6.26.3",
|
||||||
"bignumber.js": "2.4.0",
|
"bignumber.js": "2.4.0",
|
||||||
"color-hash": "^1.0.3",
|
"color-hash": "^1.0.3",
|
||||||
|
"commander": "^2.19.0",
|
||||||
"copy-webpack-plugin": "^4.5.2",
|
"copy-webpack-plugin": "^4.5.2",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
"date-fns": "^1.29.0",
|
"date-fns": "^1.29.0",
|
||||||
@ -67,7 +69,7 @@
|
|||||||
"styled-components": "^3.4.9",
|
"styled-components": "^3.4.9",
|
||||||
"styled-media-query": "^2.0.2",
|
"styled-media-query": "^2.0.2",
|
||||||
"styled-normalize": "^8.0.0",
|
"styled-normalize": "^8.0.0",
|
||||||
"trezor-connect": "^5.0.32",
|
"trezor-connect": "6.0.0",
|
||||||
"web3": "1.0.0-beta.35",
|
"web3": "1.0.0-beta.35",
|
||||||
"webpack": "^4.16.3",
|
"webpack": "^4.16.3",
|
||||||
"webpack-build-notifier": "^0.1.29",
|
"webpack-build-notifier": "^0.1.29",
|
||||||
|
@ -1,4 +1,19 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
docker build -t trezor-wallet ../
|
|
||||||
|
if [[ "$1" == "dev" || "$1" == "beta" || "$1" == "stable" ]]
|
||||||
|
then
|
||||||
|
mkdir -p ../build
|
||||||
|
rm -rf ../build/$1
|
||||||
|
docker ps -q --filter "name=trezor-wallet" | grep -q . && docker stop trezor-wallet && docker rm -fv trezor-wallet
|
||||||
|
docker build -t trezor-wallet ../ --build-arg BUILD_TYPE=$1
|
||||||
|
docker run -p 8080:8080 -d --name trezor-wallet trezor-wallet:latest
|
||||||
|
docker cp trezor-wallet:/trezor-wallet-app/build/$1 ../build/$1
|
||||||
|
docker stop trezor-wallet
|
||||||
|
docker rm trezor-wallet
|
||||||
|
echo "DONE!"
|
||||||
|
echo "Build directory: build/"$1
|
||||||
|
else
|
||||||
|
echo "invalid parameters... valid parameters are (dev, beta, stable)"
|
||||||
|
fi
|
86
scripts/s3sync.sh
Executable file
86
scripts/s3sync.sh
Executable file
@ -0,0 +1,86 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Before first use:
|
||||||
|
# Install awscli (pip install awscli)
|
||||||
|
# Configure access credentials (aws configure), region is "eu-central-1"
|
||||||
|
|
||||||
|
# Usage:
|
||||||
|
# ./s3sync.sh DESTINATION SOURCE [clear]
|
||||||
|
# @DESTINATION: required, destination server
|
||||||
|
# @SOURCE: required, build
|
||||||
|
# @CLEAR: optional, delete previous uploads
|
||||||
|
# ./s3sync.sh stage beta
|
||||||
|
# ./s3sync.sh stage stable
|
||||||
|
# ./s3sync.sh stage stable clear
|
||||||
|
# ./s3sync.sh beta beta
|
||||||
|
# ./s3sync.sh stable stable
|
||||||
|
|
||||||
|
function confirm {
|
||||||
|
read -r -p "Are you sure? [y/N] " response
|
||||||
|
if [[ $response =~ ^(yes|y)$ ]]; then
|
||||||
|
echo "let's go!"
|
||||||
|
else
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Validate params
|
||||||
|
if [ "x$1" == "x" ] || [ "x$2" == "x" ]; then
|
||||||
|
echo "Invalid params"
|
||||||
|
echo "./s3sync.sh stage|beta|stable beta|stable [clear]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate destination param
|
||||||
|
if [ "x$1" != "xstage" ] && [ "x$1" != "xbeta" ] && [ "x$1" != "xstable" ]; then
|
||||||
|
echo "Invalid destination: "$1
|
||||||
|
echo "use: stage|beta|stable"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate source param
|
||||||
|
if [ "x$2" != "xbeta" ] && [ "x$2" != "xstable" ]; then
|
||||||
|
echo "Invalid source: "$2
|
||||||
|
echo "use: beta|stable"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set source directory
|
||||||
|
if [ "x$2" == "xbeta" ]; then
|
||||||
|
SOURCE=../build/beta
|
||||||
|
|
||||||
|
elif [ "x$2" == "xstable" ]; then
|
||||||
|
SOURCE=../build/stable
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set destination
|
||||||
|
if [ "x$1" == "xstage" ]; then
|
||||||
|
BUCKET=stage.mytrezor.com
|
||||||
|
DISTRIBUTION_ID="E24M0QWO692FQL"
|
||||||
|
elif [ "x$1" == "xbeta" ]; then
|
||||||
|
BUCKET=beta.mytrezor.com
|
||||||
|
DISTRIBUTION_ID="E1PONNHWUNCQ9M"
|
||||||
|
elif [ "x$2" == "xstable" ]; then
|
||||||
|
BUCKET=wallet.mytrezor.com
|
||||||
|
DISTRIBUTION_ID="EZM01GFTITGVD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "sync "$SOURCE" with "$BUCKET"/next"
|
||||||
|
|
||||||
|
if [ "x$1" == "xbeta" ] || [ "x$1" == "xstable" ]; then
|
||||||
|
confirm
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -e
|
||||||
|
cd `dirname $0`
|
||||||
|
|
||||||
|
if [ "x$3" == "x-clear" ]; then
|
||||||
|
aws s3 sync --delete --cache-control 'public, max-age=3600' $SOURCE s3://$BUCKET/next
|
||||||
|
else
|
||||||
|
aws s3 sync --cache-control 'public, max-age=3600' $SOURCE s3://$BUCKET/next
|
||||||
|
fi
|
||||||
|
|
||||||
|
aws cloudfront create-invalidation --distribution-id $DISTRIBUTION_ID --paths '/next/*'
|
||||||
|
|
||||||
|
echo "DONE"
|
@ -3,6 +3,12 @@ const morgan = require('morgan');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
|
const commander = require('commander');
|
||||||
|
|
||||||
|
commander
|
||||||
|
.version('0.1.0')
|
||||||
|
.option('-b, --buildType [type]', 'Set the build type')
|
||||||
|
.parse(process.argv);
|
||||||
|
|
||||||
const privateKey = fs.readFileSync('server/key.pem').toString();
|
const privateKey = fs.readFileSync('server/key.pem').toString();
|
||||||
const certificate = fs.readFileSync('server/cert.pem').toString();
|
const certificate = fs.readFileSync('server/cert.pem').toString();
|
||||||
@ -16,11 +22,11 @@ const PORT = process.env.PORT || 8080;
|
|||||||
app.use(morgan(':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] :response-time ms'));
|
app.use(morgan(':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] :response-time ms'));
|
||||||
|
|
||||||
// Serve static assets
|
// Serve static assets
|
||||||
app.use(express.static(path.resolve(__dirname, '..', 'build')));
|
app.use(express.static(path.resolve(__dirname, '..', 'build', commander.buildType)));
|
||||||
|
|
||||||
// Always return the main index.html, so react-router render the route in the client
|
// Always return the main index.html, so react-router render the route in the client
|
||||||
app.get('*', (req, res) => {
|
app.get('*', (req, res) => {
|
||||||
res.sendFile(path.resolve(__dirname, '..', 'build', 'index.html'));
|
res.sendFile(path.resolve(__dirname, '..', 'build', commander.buildType, 'index.html'));
|
||||||
});
|
});
|
||||||
|
|
||||||
const httpsServer = https.createServer(credentials, app);
|
const httpsServer = https.createServer(credentials, app);
|
||||||
|
@ -10,6 +10,7 @@ import * as PENDING from 'actions/constants/pendingTx';
|
|||||||
import * as WALLET from 'actions/constants/wallet';
|
import * as WALLET from 'actions/constants/wallet';
|
||||||
import { httpRequest } from 'utils/networkUtils';
|
import { httpRequest } from 'utils/networkUtils';
|
||||||
import * as buildUtils from 'utils/build';
|
import * as buildUtils from 'utils/build';
|
||||||
|
import * as storageUtils from 'utils/storage';
|
||||||
|
|
||||||
import { findAccountTokens } from 'reducers/TokensReducer';
|
import { findAccountTokens } from 'reducers/TokensReducer';
|
||||||
import type { Account } from 'reducers/AccountsReducer';
|
import type { Account } from 'reducers/AccountsReducer';
|
||||||
@ -17,7 +18,6 @@ import type { Token } from 'reducers/TokensReducer';
|
|||||||
import type { PendingTx } from 'reducers/PendingTxReducer';
|
import type { PendingTx } from 'reducers/PendingTxReducer';
|
||||||
import type { Discovery } from 'reducers/DiscoveryReducer';
|
import type { Discovery } from 'reducers/DiscoveryReducer';
|
||||||
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
TrezorDevice,
|
TrezorDevice,
|
||||||
ThunkAction,
|
ThunkAction,
|
||||||
@ -43,22 +43,15 @@ export type StorageAction = {
|
|||||||
error: string,
|
error: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
const get = (key: string): ?string => {
|
const TYPE: 'local' = 'local';
|
||||||
try {
|
const { STORAGE_PATH } = storageUtils;
|
||||||
return window.localStorage.getItem(key);
|
const KEY_VERSION: string = `${STORAGE_PATH}version`;
|
||||||
} catch (error) {
|
const KEY_DEVICES: string = `${STORAGE_PATH}devices`;
|
||||||
// available = false;
|
const KEY_ACCOUNTS: string = `${STORAGE_PATH}accounts`;
|
||||||
return null;
|
const KEY_DISCOVERY: string = `${STORAGE_PATH}discovery`;
|
||||||
}
|
const KEY_TOKENS: string = `${STORAGE_PATH}tokens`;
|
||||||
};
|
const KEY_PENDING: string = `${STORAGE_PATH}pending`;
|
||||||
|
const KEY_BETA_MODAL: string = '/betaModalPrivacy'; // this key needs to be compatible with "parent" (old) wallet
|
||||||
const set = (key: string, value: string | boolean): void => {
|
|
||||||
try {
|
|
||||||
window.localStorage.setItem(key, value);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Local Storage ERROR: ${error}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://github.com/STRML/react-localstorage/blob/master/react-localstorage.js
|
// https://github.com/STRML/react-localstorage/blob/master/react-localstorage.js
|
||||||
// or
|
// or
|
||||||
@ -80,25 +73,25 @@ export const save = (): ThunkAction => (dispatch: Dispatch, getState: GetState):
|
|||||||
const discovery: Array<Discovery> = findDiscovery(devices, getState().discovery);
|
const discovery: Array<Discovery> = findDiscovery(devices, getState().discovery);
|
||||||
|
|
||||||
// save devices
|
// save devices
|
||||||
set('devices', JSON.stringify(devices));
|
storageUtils.set(TYPE, KEY_DEVICES, JSON.stringify(devices));
|
||||||
|
|
||||||
// save already preloaded accounts
|
// save already preloaded accounts
|
||||||
set('accounts', JSON.stringify(accounts));
|
storageUtils.set(TYPE, KEY_ACCOUNTS, JSON.stringify(accounts));
|
||||||
|
|
||||||
// save discovery state
|
// save discovery state
|
||||||
set('discovery', JSON.stringify(discovery));
|
storageUtils.set(TYPE, KEY_DISCOVERY, JSON.stringify(discovery));
|
||||||
|
|
||||||
// tokens
|
// tokens
|
||||||
set('tokens', JSON.stringify(tokens));
|
storageUtils.set(TYPE, KEY_TOKENS, JSON.stringify(tokens));
|
||||||
|
|
||||||
// pending transactions
|
// pending transactions
|
||||||
set('pending', JSON.stringify(pending));
|
storageUtils.set(TYPE, KEY_PENDING, JSON.stringify(pending));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const update = (event: StorageEvent): ThunkAction => (dispatch: Dispatch): void => {
|
export const update = (event: StorageEvent): ThunkAction => (dispatch: Dispatch): void => {
|
||||||
if (!event.newValue) return;
|
if (!event.newValue) return;
|
||||||
|
|
||||||
if (event.key === 'devices') {
|
if (event.key === KEY_DEVICES) {
|
||||||
// check if device was added/ removed
|
// check if device was added/ removed
|
||||||
// const newDevices: Array<TrezorDevice> = JSON.parse(event.newValue);
|
// const newDevices: Array<TrezorDevice> = JSON.parse(event.newValue);
|
||||||
// const myDevices: Array<TrezorDevice> = getState().connect.devices.filter(d => d.features);
|
// const myDevices: Array<TrezorDevice> = getState().connect.devices.filter(d => d.features);
|
||||||
@ -113,28 +106,28 @@ export const update = (event: StorageEvent): ThunkAction => (dispatch: Dispatch)
|
|||||||
// const diff = oldDevices.filter(d => newDevices.indexOf())
|
// const diff = oldDevices.filter(d => newDevices.indexOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === 'accounts') {
|
if (event.key === KEY_ACCOUNTS) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACCOUNT.FROM_STORAGE,
|
type: ACCOUNT.FROM_STORAGE,
|
||||||
payload: JSON.parse(event.newValue),
|
payload: JSON.parse(event.newValue),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === 'tokens') {
|
if (event.key === KEY_TOKENS) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: TOKEN.FROM_STORAGE,
|
type: TOKEN.FROM_STORAGE,
|
||||||
payload: JSON.parse(event.newValue),
|
payload: JSON.parse(event.newValue),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === 'pending') {
|
if (event.key === KEY_PENDING) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: PENDING.FROM_STORAGE,
|
type: PENDING.FROM_STORAGE,
|
||||||
payload: JSON.parse(event.newValue),
|
payload: JSON.parse(event.newValue),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === 'discovery') {
|
if (event.key === KEY_DISCOVERY) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: DISCOVERY.FROM_STORAGE,
|
type: DISCOVERY.FROM_STORAGE,
|
||||||
payload: JSON.parse(event.newValue),
|
payload: JSON.parse(event.newValue),
|
||||||
@ -142,14 +135,13 @@ export const update = (event: StorageEvent): ThunkAction => (dispatch: Dispatch)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const VERSION: string = '1';
|
|
||||||
|
|
||||||
const loadJSON = (): AsyncAction => async (dispatch: Dispatch): Promise<void> => {
|
const loadJSON = (): AsyncAction => async (dispatch: Dispatch): Promise<void> => {
|
||||||
if (typeof window.localStorage === 'undefined') return;
|
if (typeof window.localStorage === 'undefined') return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const config: Config = await httpRequest(AppConfigJSON, 'json');
|
const config: Config = await httpRequest(AppConfigJSON, 'json');
|
||||||
|
|
||||||
|
// remove ropsten testnet from config networks
|
||||||
if (!buildUtils.isDev()) {
|
if (!buildUtils.isDev()) {
|
||||||
const index = config.networks.findIndex(c => c.shortcut === 'trop');
|
const index = config.networks.findIndex(c => c.shortcut === 'trop');
|
||||||
delete config.networks[index];
|
delete config.networks[index];
|
||||||
@ -161,18 +153,6 @@ const loadJSON = (): AsyncAction => async (dispatch: Dispatch): Promise<void> =>
|
|||||||
dispatch(update(event));
|
dispatch(update(event));
|
||||||
});
|
});
|
||||||
|
|
||||||
// validate version
|
|
||||||
const version: ?string = get('version');
|
|
||||||
if (version !== VERSION) {
|
|
||||||
try {
|
|
||||||
window.localStorage.clear();
|
|
||||||
window.sessionStorage.clear();
|
|
||||||
} catch (error) {
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
set('version', VERSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
// load tokens
|
// load tokens
|
||||||
const tokens = await config.networks.reduce(async (promise: Promise<TokensCollection>, network: Network): Promise<TokensCollection> => {
|
const tokens = await config.networks.reduce(async (promise: Promise<TokensCollection>, network: Network): Promise<TokensCollection> => {
|
||||||
const collection: TokensCollection = await promise;
|
const collection: TokensCollection = await promise;
|
||||||
@ -195,9 +175,17 @@ const loadJSON = (): AsyncAction => async (dispatch: Dispatch): Promise<void> =>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const VERSION: string = '1';
|
||||||
|
|
||||||
const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||||
const devices: ?string = get('devices');
|
// validate version
|
||||||
|
const version: ?string = storageUtils.get(TYPE, KEY_VERSION);
|
||||||
|
if (version && version !== VERSION) {
|
||||||
|
storageUtils.clearAll();
|
||||||
|
}
|
||||||
|
storageUtils.set(TYPE, KEY_VERSION, VERSION);
|
||||||
|
|
||||||
|
const devices: ?string = storageUtils.get(TYPE, KEY_DEVICES);
|
||||||
if (devices) {
|
if (devices) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CONNECT.DEVICE_FROM_STORAGE,
|
type: CONNECT.DEVICE_FROM_STORAGE,
|
||||||
@ -205,7 +193,7 @@ const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const accounts: ?string = get('accounts');
|
const accounts: ?string = storageUtils.get(TYPE, KEY_ACCOUNTS);
|
||||||
if (accounts) {
|
if (accounts) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACCOUNT.FROM_STORAGE,
|
type: ACCOUNT.FROM_STORAGE,
|
||||||
@ -213,7 +201,7 @@ const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const userTokens: ?string = get('tokens');
|
const userTokens: ?string = storageUtils.get(TYPE, KEY_TOKENS);
|
||||||
if (userTokens) {
|
if (userTokens) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: TOKEN.FROM_STORAGE,
|
type: TOKEN.FROM_STORAGE,
|
||||||
@ -221,7 +209,7 @@ const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const pending: ?string = get('pending');
|
const pending: ?string = storageUtils.get(TYPE, KEY_PENDING);
|
||||||
if (pending) {
|
if (pending) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: PENDING.FROM_STORAGE,
|
type: PENDING.FROM_STORAGE,
|
||||||
@ -229,7 +217,7 @@ const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const discovery: ?string = get('discovery');
|
const discovery: ?string = storageUtils.get(TYPE, KEY_DISCOVERY);
|
||||||
if (discovery) {
|
if (discovery) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: DISCOVERY.FROM_STORAGE,
|
type: DISCOVERY.FROM_STORAGE,
|
||||||
@ -238,7 +226,7 @@ const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (buildUtils.isDev() || buildUtils.isBeta()) {
|
if (buildUtils.isDev() || buildUtils.isBeta()) {
|
||||||
const betaModal = get('/betaModalPrivacy');
|
const betaModal = Object.keys(window.localStorage).find(key => key.indexOf(KEY_BETA_MODAL) >= 0);
|
||||||
if (!betaModal) {
|
if (!betaModal) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: WALLET.SHOW_BETA_DISCLAIMER,
|
type: WALLET.SHOW_BETA_DISCLAIMER,
|
||||||
@ -258,6 +246,6 @@ export const loadData = (): ThunkAction => (dispatch: Dispatch, getState: GetSta
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const hideBetaDisclaimer = (): ThunkAction => (dispatch: Dispatch): void => {
|
export const hideBetaDisclaimer = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||||
set('/betaModalPrivacy', true);
|
storageUtils.set(TYPE, KEY_BETA_MODAL, true);
|
||||||
dispatch(loadJSON());
|
dispatch(loadJSON());
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { push, LOCATION_CHANGE } from 'react-router-redux';
|
import { push, LOCATION_CHANGE } from 'react-router-redux';
|
||||||
import { CONTEXT_NONE } from 'actions/constants/modal';
|
import { CONTEXT_NONE } from 'actions/constants/modal';
|
||||||
|
import { SET_INITIAL_URL } from 'actions/constants/wallet';
|
||||||
import { routes } from 'support/routes';
|
import { routes } from 'support/routes';
|
||||||
import * as deviceUtils from 'utils/device';
|
import * as deviceUtils from 'utils/device';
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ export const paramsValidation = (params: RouterLocationState): PayloadAction<boo
|
|||||||
if (params.hasOwnProperty('deviceInstance')) {
|
if (params.hasOwnProperty('deviceInstance')) {
|
||||||
device = devices.find(d => d.features && d.features.device_id === params.device && d.instance === parseInt(params.deviceInstance, 10));
|
device = devices.find(d => d.features && d.features.device_id === params.device && d.instance === parseInt(params.deviceInstance, 10));
|
||||||
} else {
|
} else {
|
||||||
device = devices.find(d => d.path === params.device || (d.features && d.features.device_id === params.device));
|
device = devices.find(d => ((!d.features || d.mode === 'bootloader') && d.path === params.device) || (d.features && d.features.device_id === params.device));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!device) return false;
|
if (!device) return false;
|
||||||
@ -247,6 +248,8 @@ const sortDevices = (devices: Array<TrezorDevice>): Array<TrezorDevice> => devic
|
|||||||
* Redirect to requested device
|
* Redirect to requested device
|
||||||
*/
|
*/
|
||||||
export const selectDevice = (device: TrezorDevice | Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
export const selectDevice = (device: TrezorDevice | Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
|
if (dispatch(setInitialUrl())) return;
|
||||||
|
|
||||||
const url: ?string = dispatch(getDeviceUrl(device));
|
const url: ?string = dispatch(getDeviceUrl(device));
|
||||||
if (!url) return;
|
if (!url) return;
|
||||||
|
|
||||||
@ -352,7 +355,12 @@ export const setInitialUrl = (): PayloadAction<boolean> => (dispatch: Dispatch,
|
|||||||
state: {},
|
state: {},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (valid === initialPathname) {
|
if (valid === initialPathname) {
|
||||||
|
// reset initial url
|
||||||
|
dispatch({
|
||||||
|
type: SET_INITIAL_URL,
|
||||||
|
});
|
||||||
dispatch(goto(valid));
|
dispatch(goto(valid));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
import * as storageUtils from 'utils/storage';
|
||||||
|
|
||||||
import type { State as SendFormState } from 'reducers/SendFormReducer';
|
import type { State as SendFormState } from 'reducers/SendFormReducer';
|
||||||
import type {
|
import type {
|
||||||
@ -9,52 +9,39 @@ import type {
|
|||||||
Dispatch,
|
Dispatch,
|
||||||
} from 'flowtype';
|
} from 'flowtype';
|
||||||
|
|
||||||
const TX_PREFIX: string = 'trezor:draft-tx:';
|
const TYPE: 'session' = 'session';
|
||||||
|
const { STORAGE_PATH } = storageUtils;
|
||||||
|
const KEY_TX_DRAFT: string = `${STORAGE_PATH}txdraft`;
|
||||||
|
|
||||||
|
const getTxDraftKey = (getState: GetState): string => {
|
||||||
|
const { pathname } = getState().router.location;
|
||||||
|
return `${KEY_TX_DRAFT}${pathname}`;
|
||||||
|
};
|
||||||
|
|
||||||
export const saveDraftTransaction = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
export const saveDraftTransaction = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
if (typeof window.localStorage === 'undefined') return;
|
|
||||||
|
|
||||||
const state = getState().sendForm;
|
const state = getState().sendForm;
|
||||||
if (state.untouched) return;
|
if (state.untouched) return;
|
||||||
|
|
||||||
const location = getState().router.location.pathname;
|
const key = getTxDraftKey(getState);
|
||||||
try {
|
storageUtils.set(TYPE, key, JSON.stringify(state));
|
||||||
// save state as it is
|
|
||||||
// "loadDraftTransaction" will do the validation
|
|
||||||
window.sessionStorage.setItem(`${TX_PREFIX}${location}`, JSON.stringify(state));
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Saving sessionStorage error: ${error}`);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loadDraftTransaction = (): PayloadAction<?SendFormState> => (dispatch: Dispatch, getState: GetState): ?SendFormState => {
|
export const loadDraftTransaction = (): PayloadAction<?SendFormState> => (dispatch: Dispatch, getState: GetState): ?SendFormState => {
|
||||||
if (typeof window.localStorage === 'undefined') return null;
|
const key = getTxDraftKey(getState);
|
||||||
|
const value: ?string = storageUtils.get(TYPE, key);
|
||||||
try {
|
if (!value) return null;
|
||||||
const location = getState().router.location.pathname;
|
const state: ?SendFormState = JSON.parse(value);
|
||||||
const value: string = window.sessionStorage.getItem(`${TX_PREFIX}${location}`);
|
if (!state) return null;
|
||||||
const state: ?SendFormState = JSON.parse(value);
|
// decide if draft is valid and should be returned
|
||||||
if (state) {
|
// ignore this draft if has any error
|
||||||
// decide if draft is valid and should be returned
|
if (Object.keys(state.errors).length > 0) {
|
||||||
// ignore this draft if has any error
|
storageUtils.remove(TYPE, key);
|
||||||
if (Object.keys(state.errors).length > 0) {
|
return null;
|
||||||
window.sessionStorage.removeItem(`${TX_PREFIX}${location}`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Loading sessionStorage error: ${error}`);
|
|
||||||
}
|
}
|
||||||
return null;
|
return state;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clear = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
export const clear = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
if (typeof window.localStorage === 'undefined') return;
|
const key = getTxDraftKey(getState);
|
||||||
const location = getState().router.location.pathname;
|
storageUtils.remove(TYPE, key);
|
||||||
try {
|
};
|
||||||
window.sessionStorage.removeItem(`${TX_PREFIX}${location}`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Clearing sessionStorage error: ${error}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
@ -87,7 +87,7 @@ const StyledIcon = styled(Icon)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const IconWrapper = styled.div`
|
const IconWrapper = styled.div`
|
||||||
min-width: 20px;
|
min-width: 30px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Texts = styled.div`
|
const Texts = styled.div`
|
||||||
|
@ -3,6 +3,7 @@ export const FONT_SIZE = {
|
|||||||
SMALLER: '12px',
|
SMALLER: '12px',
|
||||||
SMALL: '14px',
|
SMALL: '14px',
|
||||||
BASE: '16px',
|
BASE: '16px',
|
||||||
|
WALLET_TITLE: '18px',
|
||||||
TOP_MENU: '17px',
|
TOP_MENU: '17px',
|
||||||
WALLET_TITLE: '18px',
|
WALLET_TITLE: '18px',
|
||||||
BIG: '21px',
|
BIG: '21px',
|
||||||
|
@ -2,47 +2,47 @@ export default [
|
|||||||
{
|
{
|
||||||
id: 'btc',
|
id: 'btc',
|
||||||
coinName: 'Bitcoin',
|
coinName: 'Bitcoin',
|
||||||
url: '../#/?coin=btc',
|
url: '../?coin=btc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'bch',
|
id: 'bch',
|
||||||
coinName: 'Bitcoin Cash',
|
coinName: 'Bitcoin Cash',
|
||||||
url: '../#/?coin=bch',
|
url: '../?coin=bch',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'btg',
|
id: 'btg',
|
||||||
coinName: 'Bitcoin Gold',
|
coinName: 'Bitcoin Gold',
|
||||||
url: '../#/?coin=btg',
|
url: '../?coin=btg',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'dash',
|
id: 'dash',
|
||||||
coinName: 'Dash',
|
coinName: 'Dash',
|
||||||
url: '../#/?coin=dash',
|
url: '../?coin=dash',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'doge',
|
id: 'doge',
|
||||||
coinName: 'Dogecoin',
|
coinName: 'Dogecoin',
|
||||||
url: '../#/?coin=doge',
|
url: '../?coin=doge',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ltc',
|
id: 'ltc',
|
||||||
coinName: 'Litecoin',
|
coinName: 'Litecoin',
|
||||||
url: '../#/?coin=ltc',
|
url: '../?coin=ltc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'nmc',
|
id: 'nmc',
|
||||||
coinName: 'Namecoin',
|
coinName: 'Namecoin',
|
||||||
url: '../#/?coin=nmc',
|
url: '../?coin=nmc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'vtc',
|
id: 'vtc',
|
||||||
coinName: 'Vertcoin',
|
coinName: 'Vertcoin',
|
||||||
url: '../#/?coin=vtc',
|
url: '../?coin=vtc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'zec',
|
id: 'zec',
|
||||||
coinName: 'Zcash',
|
coinName: 'Zcash',
|
||||||
url: '../#/?coin=zec',
|
url: '../?coin=zec',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'xem',
|
id: 'xem',
|
||||||
|
@ -4,15 +4,15 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
<title>Ethereum Wallet | Trezor</title>
|
<title>Trezor Wallet</title>
|
||||||
<meta name="title" content="Trezor Wallet" />
|
<meta name="title" content="Trezor Wallet" />
|
||||||
<meta name="description" content="Trezor Wallet" />
|
<meta name="description" content="Trezor Wallet" />
|
||||||
<meta name="keywords" content="trezor wallet" />
|
<meta name="keywords" content="trezor wallet" />
|
||||||
<meta name="author" content="satoshilabs s.r.o." />
|
<meta name="author" content="SatoshiLabs s.r.o." />
|
||||||
<meta name="viewport" content="width=1024, initial-scale=1">
|
<meta name="google" value="notranslate" />
|
||||||
<!--[if lt IE 8]>
|
<!--[if lt IE 8]>
|
||||||
<link media="all" rel="stylesheet" href="/unsupported-browsers/style.css" />
|
<link media="all" rel="stylesheet" href="/unsupported-browsers/style.css" />
|
||||||
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" />
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -44,14 +44,6 @@ export default function modal(state: State = initialState, action: Action): Stat
|
|||||||
windowType: action.type,
|
windowType: action.type,
|
||||||
};
|
};
|
||||||
|
|
||||||
// device acquired
|
|
||||||
// close modal
|
|
||||||
case DEVICE.CHANGED:
|
|
||||||
if (state.context === MODAL.CONTEXT_DEVICE && action.device.path === state.device.path && action.device.status === 'occupied') {
|
|
||||||
return initialState;
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
|
|
||||||
// device connected
|
// device connected
|
||||||
// close modal if modal context is not 'device'
|
// close modal if modal context is not 'device'
|
||||||
case DEVICE.CONNECT:
|
case DEVICE.CONNECT:
|
||||||
|
@ -47,7 +47,9 @@ const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa
|
|||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case WALLET.SET_INITIAL_URL:
|
case WALLET.SET_INITIAL_URL:
|
||||||
api.dispatch(LocalStorageActions.loadData());
|
if (action.pathname) {
|
||||||
|
api.dispatch(LocalStorageActions.loadData());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case WALLET.SET_SELECTED_DEVICE:
|
case WALLET.SET_SELECTED_DEVICE:
|
||||||
// try to authorize device
|
// try to authorize device
|
||||||
|
@ -34,7 +34,7 @@ exports[`eth utils sanitizeHex 3`] = `"0x02"`;
|
|||||||
|
|
||||||
exports[`eth utils sanitizeHex 4`] = `"0x0100"`;
|
exports[`eth utils sanitizeHex 4`] = `"0x0100"`;
|
||||||
|
|
||||||
exports[`eth utils sanitizeHex 5`] = `null`;
|
exports[`eth utils sanitizeHex 5`] = `"0x0999"`;
|
||||||
|
|
||||||
exports[`eth utils sanitizeHex 6`] = `""`;
|
exports[`eth utils sanitizeHex 6`] = `""`;
|
||||||
|
|
||||||
|
@ -40,9 +40,9 @@ describe('device utils', () => {
|
|||||||
|
|
||||||
it('isWebUSB', () => {
|
it('isWebUSB', () => {
|
||||||
const data = [
|
const data = [
|
||||||
{ transport: { version: ['webusb'] } },
|
{ transport: { type: 'ParallelTransport', version: 'webusb' } },
|
||||||
{ transport: { version: ['aaaaaa'] } },
|
{ transport: { type: null, version: 'aaaaaa' } },
|
||||||
{ transport: { version: ['webusb', 'test'] } },
|
{ transport: { type: 'ParallelTransport', version: 'webusb' } },
|
||||||
];
|
];
|
||||||
|
|
||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
@ -52,8 +52,8 @@ describe('device utils', () => {
|
|||||||
|
|
||||||
it('isDisabled', () => {
|
it('isDisabled', () => {
|
||||||
const data = [
|
const data = [
|
||||||
{ selectedDevice: { features: null }, devices: [1, 2, 3], transport: { version: ['webusb', 'test'] } },
|
{ selectedDevice: { features: null }, devices: [1, 2, 3], transport: { version: 'webusb' } },
|
||||||
{ selectedDevice: { features: null }, devices: [], transport: { version: ['test'] } },
|
{ selectedDevice: { features: null }, devices: [], transport: { version: 'test' } },
|
||||||
];
|
];
|
||||||
|
|
||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
|
@ -27,7 +27,7 @@ describe('eth utils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('sanitizeHex', () => {
|
it('sanitizeHex', () => {
|
||||||
const input = ['0x2540be3ff', '1', '2', '100', 999, ''];
|
const input = ['0x2540be3ff', '1', '2', '100', '999', ''];
|
||||||
|
|
||||||
input.forEach((entry) => {
|
input.forEach((entry) => {
|
||||||
expect(ethUtils.sanitizeHex(entry)).toMatchSnapshot();
|
expect(ethUtils.sanitizeHex(entry)).toMatchSnapshot();
|
||||||
|
62
src/utils/storage.js
Normal file
62
src/utils/storage.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/* @flow */
|
||||||
|
|
||||||
|
// Copy-paste from mytrezor (old wallet)
|
||||||
|
// https://github.com/satoshilabs/mytrezor/blob/develop/app/scripts/storage/BackendLocalStorage.js
|
||||||
|
export const getStoragePath = (): string => {
|
||||||
|
const regexHash = /^([^#]*)#.*$/;
|
||||||
|
const path = window.location.href.replace(regexHash, '$1');
|
||||||
|
const regexStart = /^[^:]*:\/\/[^/]*\//;
|
||||||
|
return path.replace(regexStart, '/');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const STORAGE_PATH: string = getStoragePath();
|
||||||
|
|
||||||
|
type StorageType = 'local' | 'session';
|
||||||
|
|
||||||
|
export const get: <T>(type: StorageType, key: string) => T | null = (type, key) => {
|
||||||
|
const storage = type === 'local' ? window.localStorage : window.sessionStorage;
|
||||||
|
try {
|
||||||
|
return storage.getItem(key);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Get ${type} storage: ${error}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const set = (type: StorageType, key: string, value: string | number | boolean): void => {
|
||||||
|
const storage = type === 'local' ? window.localStorage : window.sessionStorage;
|
||||||
|
try {
|
||||||
|
storage.setItem(key, value);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Save ${type} storage: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const remove = (type: StorageType, key: string): void => {
|
||||||
|
const storage = type === 'local' ? window.localStorage : window.sessionStorage;
|
||||||
|
try {
|
||||||
|
storage.removeItem(key);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Remove ${type} storage: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clearAll = (type: ?StorageType): void => {
|
||||||
|
let clearLocal: boolean = true;
|
||||||
|
let clearSession: boolean = true;
|
||||||
|
if (typeof type === 'string') {
|
||||||
|
clearLocal = type === 'local';
|
||||||
|
clearSession = !clearLocal;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (clearLocal) {
|
||||||
|
Object.keys(window.localStorage).forEach(key => key.indexOf(STORAGE_PATH) >= 0 && window.localStorage.removeItem(key));
|
||||||
|
}
|
||||||
|
if (clearSession) {
|
||||||
|
Object.keys(window.sessionStorage).forEach(key => key.indexOf(STORAGE_PATH) >= 0 && window.sessionStorage.removeItem(key));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Clearing sessionStorage error: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
@ -135,7 +135,7 @@ class InstallBridge extends PureComponent<Props, State> {
|
|||||||
latestVersion: props.transport.bridge.version.join('.'),
|
latestVersion: props.transport.bridge.version.join('.'),
|
||||||
installers,
|
installers,
|
||||||
target: currentTarget || installers[0],
|
target: currentTarget || installers[0],
|
||||||
uri: 'https://wallet.trezor.io/data/',
|
uri: 'https://data.trezor.io/',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ const AddAccountIconWrapper = styled.div`
|
|||||||
const DiscoveryStatusWrapper = styled.div`
|
const DiscoveryStatusWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
font-size: ${FONT_SIZE.SMALL};
|
font-size: ${FONT_SIZE.SMALL};
|
||||||
padding: ${LEFT_NAVIGATION_ROW.PADDING};
|
padding: ${LEFT_NAVIGATION_ROW.PADDING};
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@ -164,15 +164,34 @@ const AccountMenu = (props: Props) => {
|
|||||||
let discoveryStatus = null;
|
let discoveryStatus = null;
|
||||||
const discovery = props.discovery.find(d => d.deviceState === selected.state && d.network === location.state.network);
|
const discovery = props.discovery.find(d => d.deviceState === selected.state && d.network === location.state.network);
|
||||||
|
|
||||||
if (discovery) {
|
if (discovery && discovery.completed) {
|
||||||
if (discovery.completed) {
|
// TODO: add only if last one is not empty
|
||||||
// TODO: add only if last one is not empty
|
//if (selectedAccounts.length > 0 && selectedAccounts[selectedAccounts.length - 1])
|
||||||
//if (selectedAccounts.length > 0 && selectedAccounts[selectedAccounts.length - 1])
|
const lastAccount = deviceAccounts[deviceAccounts.length - 1];
|
||||||
const lastAccount = deviceAccounts[deviceAccounts.length - 1];
|
if (lastAccount && (new BigNumber(lastAccount.balance).greaterThan(0) || lastAccount.nonce > 0)) {
|
||||||
if (lastAccount && (new BigNumber(lastAccount.balance).greaterThan(0) || lastAccount.nonce > 0)) {
|
discoveryStatus = (
|
||||||
discoveryStatus = (
|
<Row onClick={props.addAccount}>
|
||||||
<Row onClick={props.addAccount}>
|
<RowAddAccountWrapper>
|
||||||
<RowAddAccountWrapper>
|
<AddAccountIconWrapper>
|
||||||
|
<Icon
|
||||||
|
icon={ICONS.PLUS}
|
||||||
|
size={24}
|
||||||
|
color={colors.TEXT_SECONDARY}
|
||||||
|
/>
|
||||||
|
</AddAccountIconWrapper>
|
||||||
|
Add account
|
||||||
|
</RowAddAccountWrapper>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
discoveryStatus = (
|
||||||
|
<Tooltip
|
||||||
|
maxWidth={200}
|
||||||
|
content={<TooltipContent>To add a new account, last account must have some transactions.</TooltipContent>}
|
||||||
|
placement="bottom"
|
||||||
|
>
|
||||||
|
<Row>
|
||||||
|
<RowAddAccountWrapper disabled>
|
||||||
<AddAccountIconWrapper>
|
<AddAccountIconWrapper>
|
||||||
<Icon
|
<Icon
|
||||||
icon={ICONS.PLUS}
|
icon={ICONS.PLUS}
|
||||||
@ -183,52 +202,31 @@ const AccountMenu = (props: Props) => {
|
|||||||
Add account
|
Add account
|
||||||
</RowAddAccountWrapper>
|
</RowAddAccountWrapper>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
</Tooltip>
|
||||||
} else {
|
|
||||||
discoveryStatus = (
|
|
||||||
<Tooltip
|
|
||||||
maxWidth={200}
|
|
||||||
content={<TooltipContent>To add a new account, last account must have some transactions.</TooltipContent>}
|
|
||||||
placement="bottom"
|
|
||||||
>
|
|
||||||
<Row>
|
|
||||||
<RowAddAccountWrapper disabled>
|
|
||||||
<AddAccountIconWrapper>
|
|
||||||
<Icon
|
|
||||||
icon={ICONS.PLUS}
|
|
||||||
size={24}
|
|
||||||
color={colors.TEXT_SECONDARY}
|
|
||||||
/>
|
|
||||||
</AddAccountIconWrapper>
|
|
||||||
Add account
|
|
||||||
</RowAddAccountWrapper>
|
|
||||||
</Row>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (!selected.connected || !selected.available) {
|
|
||||||
discoveryStatus = (
|
|
||||||
<Row>
|
|
||||||
<DiscoveryStatusWrapper>
|
|
||||||
Accounts could not be loaded
|
|
||||||
<DiscoveryStatusText>
|
|
||||||
{`Connect ${selected.instanceLabel} device`}
|
|
||||||
</DiscoveryStatusText>
|
|
||||||
</DiscoveryStatusWrapper>
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
discoveryStatus = (
|
|
||||||
<Row>
|
|
||||||
<DiscoveryLoadingWrapper>
|
|
||||||
<Loader size={20} />
|
|
||||||
<DiscoveryLoadingText>
|
|
||||||
Loading...
|
|
||||||
</DiscoveryLoadingText>
|
|
||||||
</DiscoveryLoadingWrapper>
|
|
||||||
</Row>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if (!selected.connected) {
|
||||||
|
discoveryStatus = (
|
||||||
|
<Row>
|
||||||
|
<DiscoveryStatusWrapper>
|
||||||
|
Accounts could not be loaded
|
||||||
|
<DiscoveryStatusText>
|
||||||
|
{`Connect ${selected.instanceLabel} device`}
|
||||||
|
</DiscoveryStatusText>
|
||||||
|
</DiscoveryStatusWrapper>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
discoveryStatus = (
|
||||||
|
<Row>
|
||||||
|
<DiscoveryLoadingWrapper>
|
||||||
|
<Loader size={20} />
|
||||||
|
<DiscoveryLoadingText>
|
||||||
|
Loading...
|
||||||
|
</DiscoveryLoadingText>
|
||||||
|
</DiscoveryLoadingWrapper>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -6,7 +6,7 @@ import React, { PureComponent } from 'react';
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
pathname: string;
|
pathname: string;
|
||||||
wrapper: ?HTMLElement;
|
wrapper: () => ?HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
@ -71,8 +71,8 @@ class Indicator extends PureComponent<Props, State> {
|
|||||||
handleResize: () => void;
|
handleResize: () => void;
|
||||||
|
|
||||||
reposition(resetAnimation: boolean = true) {
|
reposition(resetAnimation: boolean = true) {
|
||||||
if (!this.props.wrapper) return;
|
const wrapper = this.props.wrapper();
|
||||||
const { wrapper } = this.props;
|
if (!wrapper) return;
|
||||||
const active = wrapper.querySelector('.active');
|
const active = wrapper.querySelector('.active');
|
||||||
if (!active) return;
|
if (!active) return;
|
||||||
const bounds = active.getBoundingClientRect();
|
const bounds = active.getBoundingClientRect();
|
||||||
|
@ -65,7 +65,7 @@ class TopNavigationAccount extends React.PureComponent<Props> {
|
|||||||
<StyledNavLink to={`${basePath}/receive`}>Receive</StyledNavLink>
|
<StyledNavLink to={`${basePath}/receive`}>Receive</StyledNavLink>
|
||||||
<StyledNavLink to={`${basePath}/send`}>Send</StyledNavLink>
|
<StyledNavLink to={`${basePath}/send`}>Send</StyledNavLink>
|
||||||
<StyledNavLink to={`${basePath}/signverify`}>Sign & Verify</StyledNavLink>
|
<StyledNavLink to={`${basePath}/signverify`}>Sign & Verify</StyledNavLink>
|
||||||
<Indicator pathname={pathname} wrapper={this.wrapper} />
|
<Indicator pathname={pathname} wrapper={() => this.wrapper} />
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -105,6 +105,7 @@ const UpdateFeeWrapper = styled.span`
|
|||||||
|
|
||||||
const StyledLink = styled(Link)`
|
const StyledLink = styled(Link)`
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
|
white-space: nowrap;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ToggleAdvancedSettingsWrapper = styled.div`
|
const ToggleAdvancedSettingsWrapper = styled.div`
|
||||||
|
@ -13,6 +13,7 @@ import Content from 'views/Wallet/components/Content';
|
|||||||
import CoinLogo from 'components/images/CoinLogo';
|
import CoinLogo from 'components/images/CoinLogo';
|
||||||
import * as stateUtils from 'reducers/utils';
|
import * as stateUtils from 'reducers/utils';
|
||||||
import Link from 'components/Link';
|
import Link from 'components/Link';
|
||||||
|
import { FONT_WEIGHT, FONT_SIZE } from 'config/variables';
|
||||||
import AccountBalance from './components/Balance';
|
import AccountBalance from './components/Balance';
|
||||||
import AddedToken from './components/Token';
|
import AddedToken from './components/Token';
|
||||||
|
|
||||||
@ -38,9 +39,16 @@ const StyledTooltip = styled(Tooltip)`
|
|||||||
|
|
||||||
const AccountName = styled.div`
|
const AccountName = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const AccountTitle = styled.div`
|
||||||
|
font-size: ${FONT_SIZE.WALLET_TITLE};
|
||||||
|
font-weight: ${FONT_WEIGHT.BASE};
|
||||||
|
color: ${colors.WALLET_TITLE};
|
||||||
|
`;
|
||||||
|
|
||||||
const StyledCoinLogo = styled(CoinLogo)`
|
const StyledCoinLogo = styled(CoinLogo)`
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
`;
|
`;
|
||||||
@ -83,7 +91,7 @@ const AccountSummary = (props: Props) => {
|
|||||||
<AccountHeading>
|
<AccountHeading>
|
||||||
<AccountName>
|
<AccountName>
|
||||||
<StyledCoinLogo network={account.network} />
|
<StyledCoinLogo network={account.network} />
|
||||||
<H2>Account #{parseInt(account.index, 10) + 1}</H2>
|
<AccountTitle>Account #{parseInt(account.index, 10) + 1}</AccountTitle>
|
||||||
</AccountName>
|
</AccountName>
|
||||||
<Link href={explorerLink} isGray>See full transaction history</Link>
|
<Link href={explorerLink} isGray>See full transaction history</Link>
|
||||||
</AccountHeading>
|
</AccountHeading>
|
||||||
|
10
yarn.lock
10
yarn.lock
@ -2570,6 +2570,10 @@ commander@^2.13.0:
|
|||||||
version "2.18.0"
|
version "2.18.0"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970"
|
||||||
|
|
||||||
|
commander@^2.19.0:
|
||||||
|
version "2.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
|
||||||
|
|
||||||
commander@^2.8.1:
|
commander@^2.8.1:
|
||||||
version "2.17.1"
|
version "2.17.1"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
||||||
@ -10217,9 +10221,9 @@ tr46@^1.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
punycode "^2.1.0"
|
punycode "^2.1.0"
|
||||||
|
|
||||||
trezor-connect@^5.0.32:
|
trezor-connect@6.0.0:
|
||||||
version "5.0.32"
|
version "6.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/trezor-connect/-/trezor-connect-5.0.32.tgz#a8077f46653fec16d8dc25358e157ed1048a4def"
|
resolved "https://registry.yarnpkg.com/trezor-connect/-/trezor-connect-6.0.0.tgz#2a45336f29a4a3f2a8ad2d121363b0e7a1b767ef"
|
||||||
dependencies:
|
dependencies:
|
||||||
babel-runtime "^6.26.0"
|
babel-runtime "^6.26.0"
|
||||||
events "^1.1.1"
|
events "^1.1.1"
|
||||||
|
Loading…
Reference in New Issue
Block a user