1
0
mirror of https://github.com/trezor/trezor-wallet synced 2024-11-24 09:18:09 +00:00
This commit is contained in:
Szymon Lesisz 2018-03-08 17:10:53 +01:00
parent cb83b2b7b6
commit 8ed54612e8
99 changed files with 1894 additions and 1177 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -21,29 +21,32 @@
}, },
"dependencies": { "dependencies": {
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"bowser": "^1.9.2",
"color-hash": "^1.0.3", "color-hash": "^1.0.3",
"ethereumjs-tx": "^1.3.3", "ethereumjs-tx": "^1.3.3",
"ethereumjs-units": "^0.2.0", "ethereumjs-units": "^0.2.0",
"ethereumjs-util": "^5.1.2", "ethereumjs-util": "^5.1.4",
"hdkey": "0.7.1", "hdkey": "^0.8.0",
"path-to-regexp": "^2.1.0", "path-to-regexp": "^2.1.0",
"raf": "^3.4.0", "raf": "^3.4.0",
"raven-js": "^3.22.3",
"rc-tooltip": "^3.7.0", "rc-tooltip": "^3.7.0",
"react": "^16.1.1", "react": "^16.2.0",
"react-blockies": "^1.2.2", "react-blockies": "^1.2.2",
"react-css-transition": "^0.7.4", "react-css-transition": "^0.7.4",
"react-dom": "^16.1.1", "react-dom": "^16.2.0",
"react-ellipsis-text": "^1.0.0", "react-ellipsis-text": "^1.0.0",
"react-hot-loader": "^3.1.3", "react-hot-loader": "^3.1.3",
"react-qr-svg": "^2.1.0", "react-qr-svg": "^2.1.0",
"react-redux": "^5.0.6", "react-redux": "^5.0.7",
"react-router-dom": "^4.2.2", "react-router-dom": "^4.2.2",
"react-router-redux": "next", "react-router-redux": "next",
"react-scale-text": "^1.2.2", "react-scale-text": "^1.2.2",
"react-select": "^1.1.0", "react-select": "^1.2.1",
"react-transition-group": "^2.2.1", "react-transition-group": "^2.2.1",
"redux": "^3.7.2", "redux": "^3.7.2",
"redux-logger": "^3.0.6", "redux-logger": "^3.0.6",
"redux-raven-middleware": "^1.2.0",
"redux-thunk": "^2.2.0", "redux-thunk": "^2.2.0",
"web3": "^0.19.0" "web3": "^0.19.0"
}, },
@ -56,22 +59,22 @@
"babel-plugin-transform-runtime": "^6.23.0", "babel-plugin-transform-runtime": "^6.23.0",
"babel-plugin-webpack-alias": "^2.1.2", "babel-plugin-webpack-alias": "^2.1.2",
"babel-preset-env": "^1.6.0", "babel-preset-env": "^1.6.0",
"copy-webpack-plugin": "^4.0.1", "copy-webpack-plugin": "^4.4.1",
"css-loader": "^0.28.4", "css-loader": "^0.28.9",
"express": "^4.15.3", "express": "^4.15.3",
"extract-text-webpack-plugin": "^3.0.0", "extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^0.11.2", "file-loader": "^1.1.9",
"html-webpack-plugin": "^2.29.0", "html-webpack-plugin": "^2.29.0",
"json-loader": "^0.5.7", "json-loader": "^0.5.7",
"less": "^2.7.2", "less": "^3.0.1",
"less-loader": "^4.0.5", "less-loader": "^4.0.5",
"open": "^0.0.5", "open": "^0.0.5",
"style-loader": "^0.18.2", "style-loader": "^0.20.2",
"webpack": "^3.4.1", "webpack": "3.11.0",
"webpack-dev-middleware": "^1.11.0", "webpack-dev-middleware": "2.0.5",
"webpack-hot-middleware": "^2.18.0", "webpack-hot-middleware": "2.21.0",
"webpack-merge": "^4.1.1", "webpack-merge": "^4.1.1",
"whatwg-fetch": "^2.0.3", "whatwg-fetch": "^2.0.3",
"yargs": "^10.0.3" "yargs": "11.0.0"
} }
} }

View File

@ -25,9 +25,8 @@
"coins": [ "coins": [
{ {
"name": "Ethereum Ropsten", "name": "Ethereum Ropsten",
"symbol": "eth", "symbol": "tETH",
"network": "ropsten-eth", "network": "ropsten",
"shortcut": "eth",
"bip44": "m/44'/60'/0'/0", "bip44": "m/44'/60'/0'/0",
"defaultGasPrice": 64, "defaultGasPrice": 64,
"defaultGasLimit": 21000, "defaultGasLimit": 21000,
@ -38,16 +37,15 @@
"urls": [ "urls": [
"https://ropsten.infura.io/QGyVKozSUEh2YhL4s2G4", "https://ropsten.infura.io/QGyVKozSUEh2YhL4s2G4",
"http://10.34.2.5:8545" "http://10.34.2.5:8545"
], ]
"explorer": "https://blockexplorer.com"
} }
] ],
"explorer": "https://ropsten.etherscan.io"
}, },
{ {
"name": "Ethereum Rinkeby", "name": "Ethereum Rinkeby",
"symbol": "etc", "symbol": "tETH",
"network": "ropsten-eth", "network": "rinkeby",
"shortcut": "etc",
"bip44": "m/44'/61'/0'/0", "bip44": "m/44'/61'/0'/0",
"defaultGasPrice": 64, "defaultGasPrice": 64,
"defaultGasLimit": 21000, "defaultGasLimit": 21000,
@ -56,19 +54,28 @@
{ {
"name": "TREZOR Wallet - Ethereum", "name": "TREZOR Wallet - Ethereum",
"urls": [ "urls": [
"https://rinkeby.infura.io/QGyVKozSUEh2YhL4s2G4", "https://rinkeby.infura.io/QGyVKozSUEh2YhL4s2G4"
"http://10.34.2.5:8545" ]
],
"explorer": "https://blockexplorer.com"
} }
] ],
"explorer": "https://rinkeby.etherscan.io"
} }
], ],
"fiatValueTickers": [ "fiatValueTickers": [
{
"network": "ropsten",
"url": "https://api.coinmarketcap.com/v1/ticker/ethereum/"
},
{
"network": "rinkeby",
"url": "https://api.coinmarketcap.com/v1/ticker/ethereum-classic/"
}
], ],
"bridge": { "bridge": {
"url": "https://localback.net:21324", "url": "https://localback.net:21324",
"configUrl": "data/config_signed.bin", "configUrl": "data/config_signed.bin",
@ -76,6 +83,20 @@
}, },
"extensionId": "jcjjhjgimijdkoamemaghajlhegmoclj", "extensionId": "jcjjhjgimijdkoamemaghajlhegmoclj",
"storageVersion": "1.1.0", "storageVersion": "1.1.0",
"metadataVersion": "1.0.0" "metadataVersion": "1.0.0",
"supportedBrowsers": {
"chrome": {
"version": 59,
"download": "https://www.google.com/chrome/",
"update": "https://support.google.com/chrome/answer/95414"
},
"firefox": {
"version": 54,
"download": "https://www.mozilla.org/en-US/firefox/new/",
"update": "https://support.mozilla.org/en-US/kb/update-firefox-latest-version"
}
}
} }

Binary file not shown.

View File

@ -1,21 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="icomoon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="819.2" descent="-204.8" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" d="" />
<glyph unicode="&#x2b;" glyph-name="plus" d="M384 951.467h170.667q17.92 0 30.293-12.373t12.373-30.293v-298.667h298.667q17.92 0 30.293-12.373t12.373-30.293v-170.667q0-17.92-12.373-30.293t-30.293-12.373h-298.667v-298.667q0-17.92-12.373-30.293t-30.293-12.373h-170.667q-17.92 0-30.293 12.373t-12.373 30.293v298.667h-298.667q-17.92 0-30.293 12.373t-12.373 30.293v170.667q0 17.92 12.373 30.293t30.293 12.373h298.667v298.667q0 17.92 12.373 30.293t30.293 12.373z" />
<glyph unicode="&#x5f;" glyph-name="trezor" horiz-adv-x="679" d="M339.471 896c-142.287 0-257.634-115.347-257.634-257.634v-96.329c-49.928-8.931-81.837-21.275-81.837-37.035v-513.942l335.777-119.061 343.165 119.061v513.942c0 15.76-31.909 28.104-81.837 37.035v96.329c0 142.287-115.347 257.634-257.634 257.634zM339.471 789.915c83.698 0 151.549-67.852 151.549-151.549v-83.92c-94.502 6.876-208.597 6.876-303.099 0v83.92c0 83.698 67.852 151.549 151.549 151.549zM333.030 430.554c117.072-0.19 233.765-8.903 233.765-25.574v-324.695l-231.208-85.531-238.596 85.531v324.695c1.515 17.428 118.966 25.763 236.038 25.574z" />
<glyph unicode="&#xe019;" glyph-name="settings" d="M512 1014.613q28.16 0 63.147-4.267l32.427-129.707 4.267-0.853q41.813-11.947 80.213-33.28l4.267-1.707 114.347 68.267q52.053-40.96 88.747-89.6l-68.267-114.347 2.56-4.267q21.333-37.547 33.28-79.36l0.853-5.12 129.707-32.427q4.267-36.693 4.267-62.293 0-29.013-4.267-63.147l-129.707-32.427-0.853-5.12q-12.8-41.813-33.28-79.36l-2.56-4.267 68.267-114.347q-40.96-52.053-88.747-89.6l-114.347 69.12-4.267-2.56q-37.547-21.333-80.213-33.28l-4.267-1.707-32.427-128.853q-36.693-4.267-63.147-4.267-28.16 0-63.147 4.267l-32.427 128.853-4.267 1.707q-41.813 11.947-80.213 33.28l-4.267 2.56-114.347-69.12q-51.2 40.96-88.747 89.6l68.267 114.347-2.56 4.267q-21.333 38.4-32.427 79.36l-1.707 5.12-128.853 32.427q-5.12 35.84-5.12 63.147 0 28.16 5.12 62.293l128.853 32.427 1.707 5.12q11.093 40.96 32.427 79.36l2.56 4.267-68.267 114.347q40.107 52.053 89.6 89.6l113.493-68.267 4.267 1.707q38.4 21.333 80.213 33.28l4.267 0.853 32.427 129.707q36.693 4.267 63.147 4.267zM512 708.267q-75.947 0-129.707-53.76t-53.76-129.28 53.76-129.28 129.707-53.76 129.707 53.76 53.76 129.28-53.76 129.28-129.707 53.76z" />
<glyph unicode="&#xe031;" glyph-name="refresh" d="M808.107 917.333l115.2 115.2q5.973 5.973 10.667 4.267t4.693-11.093v-308.053q0-9.387-6.4-15.787t-15.787-6.4h-308.053q-9.387 0-11.093 4.693t4.267 10.667l114.347 114.347q-93.867 64-203.947 64-98.987 0-183.040-48.64t-132.693-132.693-48.64-183.040h-128q0 99.84 38.827 191.147t104.96 157.44 157.44 104.96 191.147 38.827q163.84 0 296.107-99.84zM876.373 524.8h128q0-99.84-38.827-191.147t-104.96-157.44-157.44-104.96-191.147-38.827q-163.84 0-296.96 100.693l-114.347-114.347q-5.973-6.827-10.667-4.693t-4.693 10.667v307.2q0 9.387 6.4 15.787t15.787 6.4h307.2q8.533 0 10.667-4.693t-4.693-10.667l-113.493-113.493q93.867-64.853 204.8-64.853 98.987 0 183.040 48.64t132.693 132.693 48.64 183.040z" />
<glyph unicode="&#xe105;" glyph-name="eye" d="M600 966q82 0 160.5-22.5t140-59 116.5-82.5 94.5-95 68-95 42.5-82.5 14-57.5-14-57.5-43-82.5-68.5-95-94.5-95-116.5-82.5-140-59-159.5-22.5-159.5 22.5-140 59-116.5 82.5-94.5 95-68.5 95-43 82.5-14 57.5 14 57.5 42.5 82.5 68 95 94.5 95 116.5 82.5 140 59 160.5 22.5zM888 701q-15 15-18 12t5-22q25-57 25-119 0-124-88-212t-212-88-212 88-88 212q0 59 23 114 8 19 4.5 22t-17.5-12q-70-69-160-184-13-16-15-40.5t9-42.5q22-36 47-71t70-82 92.5-81 113-58.5 133.5-24.5 133.5 24 113 58.5 92.5 81.5 70 81.5 47 70.5q11 18 9 42.5t-14 41.5q-90 117-163 189zM448 599l-35-36q-15-15-19.5-38.5t4.5-41.5q37-68 93-116 16-13 38.5-11t36.5 17l35 34q14 15 12.5 33.5t-16.5 33.5q-44 44-89 117-11 18-28 20t-32-12z" />
<glyph unicode="&#xe113;" glyph-name="arrow-up" d="M938.667 363.52l-168.96-169.813-301.227 301.227-301.227-301.227-168.107 169.813 470.187 470.187z" />
<glyph unicode="&#xe114;" glyph-name="arrow-dn" d="M939.52 686.080l-469.333-470.187-470.187 470.187 168.96 169.813 301.227-301.227 301.227 301.227z" />
<glyph unicode="&#xea07;" glyph-name="warning" d="M512 739.226l429.102-855.226h-858.206l429.104 855.226zM512 832c-22.070 0-44.14-14.882-60.884-44.648l-437.074-871.112c-33.486-59.532-5-108.24 63.304-108.24h869.308c68.3 0 96.792 48.708 63.3 108.24h0.002l-437.074 871.112c-16.742 29.766-38.812 44.648-60.882 44.648v0zM576 0c0-35.346-28.654-64-64-64s-64 28.654-64 64c0 35.346 28.654 64 64 64s64-28.654 64-64zM512 128c-35.346 0-64 28.654-64 64v192c0 35.346 28.654 64 64 64s64-28.654 64-64v-192c0-35.346-28.654-64-64-64z" />
<glyph unicode="&#xea0c;" glyph-name="info" d="M448 528c0 26.4 21.6 48 48 48h32c26.4 0 48-21.6 48-48v-32c0-26.4-21.6-48-48-48h-32c-26.4 0-48 21.6-48 48v32zM640 64h-256v64h64v192h-64v64h192v-256h64zM512 832c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512-96c-229.75 0-416 186.25-416 416s186.25 416 416 416 416-186.25 416-416-186.25-416-416-416z" />
<glyph unicode="&#xea0f;" glyph-name="cross" d="M1014.662 9.34c-0.004 0.004-0.008 0.008-0.012 0.010l-310.644 310.65 310.644 310.65c0.004 0.004 0.008 0.006 0.012 0.010 3.344 3.346 5.762 7.254 7.312 11.416 4.246 11.376 1.824 24.682-7.324 33.83l-146.746 146.746c-9.148 9.146-22.45 11.566-33.828 7.32-4.16-1.55-8.070-3.968-11.418-7.31 0-0.004-0.004-0.006-0.008-0.010l-310.648-310.652-310.648 310.65c-0.004 0.004-0.006 0.006-0.010 0.010-3.346 3.342-7.254 5.76-11.414 7.31-11.38 4.248-24.682 1.826-33.83-7.32l-146.748-146.748c-9.148-9.148-11.568-22.452-7.322-33.828 1.552-4.16 3.97-8.072 7.312-11.416 0.004-0.002 0.006-0.006 0.010-0.010l310.65-310.648-310.65-310.652c-0.002-0.004-0.006-0.006-0.008-0.010-3.342-3.346-5.76-7.254-7.314-11.414-4.248-11.376-1.826-24.682 7.322-33.83l146.748-146.746c9.15-9.148 22.452-11.568 33.83-7.322 4.16 1.552 8.070 3.97 11.416 7.312 0.002 0.004 0.006 0.006 0.010 0.010l310.648 310.65 310.648-310.65c0.004-0.002 0.008-0.006 0.012-0.008 3.348-3.344 7.254-5.762 11.414-7.314 11.378-4.246 24.684-1.826 33.828 7.322l146.746 146.748c9.148 9.148 11.57 22.454 7.324 33.83-1.552 4.16-3.97 8.068-7.314 11.414z" />
<glyph unicode="&#xea10;" glyph-name="checkmark" d="M864 704l-480-480-224 224-160-160 384-384 640 640z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -18,24 +18,25 @@
<glyph unicode="&#xe908;" glyph-name="icon-warning" d="M795.616 224.992l-264.896 465.44c-10.272 18.080-27.168 18.080-37.504 0l-264.864-465.44c-10.272-18.176-1.696-32.992 19.040-32.992h529.184c20.8 0 29.376 14.816 19.040 32.992zM549.76 286.88c0-10.464-8.48-18.976-18.912-18.976h-37.792c-10.336 0-18.912 8.512-18.912 18.976v37.952c0 10.464 8.576 18.976 18.912 18.976h37.792c10.4 0 18.912-8.544 18.912-18.976v-37.952zM549.76 400.736c0-10.464-8.48-18.976-18.912-18.976h-37.792c-10.336 0-18.912 8.512-18.912 18.976v113.856c0 10.464 8.576 18.976 18.912 18.976h37.792c10.4 0 18.912-8.544 18.912-18.976v-113.856z" /> <glyph unicode="&#xe908;" glyph-name="icon-warning" d="M795.616 224.992l-264.896 465.44c-10.272 18.080-27.168 18.080-37.504 0l-264.864-465.44c-10.272-18.176-1.696-32.992 19.040-32.992h529.184c20.8 0 29.376 14.816 19.040 32.992zM549.76 286.88c0-10.464-8.48-18.976-18.912-18.976h-37.792c-10.336 0-18.912 8.512-18.912 18.976v37.952c0 10.464 8.576 18.976 18.912 18.976h37.792c10.4 0 18.912-8.544 18.912-18.976v-37.952zM549.76 400.736c0-10.464-8.48-18.976-18.912-18.976h-37.792c-10.336 0-18.912 8.512-18.912 18.976v113.856c0 10.464 8.576 18.976 18.912 18.976h37.792c10.4 0 18.912-8.544 18.912-18.976v-113.856z" />
<glyph unicode="&#xe909;" glyph-name="icon-arrow-down" d="M757.216 539.072l-219.616-237.44c-8.128-8.576-19.296-13.632-31.040-13.632-11.744 0.288-23.2 5.056-31.040 13.664l-208.768 227.040c-15.36 16.928-14.176 43.040 3.008 58.176 16.864 15.424 43.392 13.952 59.040-2.656l177.76-193.504 188.608 203.904c7.52 8 18.080 12.768 29.216 13.344 11.456 0.608 21.696-3.264 30.112-10.688 16.896-15.456 18.080-41.568 2.72-58.208z" /> <glyph unicode="&#xe909;" glyph-name="icon-arrow-down" d="M757.216 539.072l-219.616-237.44c-8.128-8.576-19.296-13.632-31.040-13.632-11.744 0.288-23.2 5.056-31.040 13.664l-208.768 227.040c-15.36 16.928-14.176 43.040 3.008 58.176 16.864 15.424 43.392 13.952 59.040-2.656l177.76-193.504 188.608 203.904c7.52 8 18.080 12.768 29.216 13.344 11.456 0.608 21.696-3.264 30.112-10.688 16.896-15.456 18.080-41.568 2.72-58.208z" />
<glyph unicode="&#xe90a;" glyph-name="icon-close" d="M754.816 270.080c17.6-17.6 17.6-46.72 0-64.64-8.96-8.64-20.48-13.44-32.64-13.44s-23.68 4.8-32.32 13.44l-177.888 177.92-177.888-177.92c-16.32-16.96-47.040-17.6-64.64 0-17.92 17.92-17.92 47.040 0 64.64l178.208 177.92-178.208 177.92c-17.92 17.92-17.92 46.72 0 64.64 17.28 17.28 47.36 17.28 64.64 0l177.888-177.92 177.888 177.92c17.92 17.92 47.040 17.92 64.96 0 17.6-17.92 17.6-46.72 0-64.64l-178.24-177.92 178.24-177.92z" /> <glyph unicode="&#xe90a;" glyph-name="icon-close" d="M754.816 270.080c17.6-17.6 17.6-46.72 0-64.64-8.96-8.64-20.48-13.44-32.64-13.44s-23.68 4.8-32.32 13.44l-177.888 177.92-177.888-177.92c-16.32-16.96-47.040-17.6-64.64 0-17.92 17.92-17.92 47.040 0 64.64l178.208 177.92-178.208 177.92c-17.92 17.92-17.92 46.72 0 64.64 17.28 17.28 47.36 17.28 64.64 0l177.888-177.92 177.888 177.92c17.92 17.92 47.040 17.92 64.96 0 17.6-17.92 17.6-46.72 0-64.64l-178.24-177.92 178.24-177.92z" />
<glyph unicode="&#xe90b;" glyph-name="icon-arrow-left" d="M603.072 202.784l-237.44 219.616c-8.576 8.128-13.632 19.296-13.632 31.040 0.288 11.744 5.056 23.2 13.664 31.040l227.040 208.768c16.928 15.36 43.040 14.176 58.176-3.008 15.424-16.864 13.952-43.392-2.656-59.040l-193.504-177.76 203.904-188.608c8-7.52 12.768-18.080 13.344-29.216 0.608-11.456-3.264-21.696-10.688-30.112-15.456-16.896-41.568-18.080-58.208-2.72z" /> <glyph unicode="&#xe90b;" glyph-name="icon-arrow-up" d="M757.216 356.928l-219.616 237.44c-8.128 8.576-19.296 13.632-31.040 13.632-11.744-0.288-23.2-5.056-31.040-13.664l-208.768-227.040c-15.36-16.928-14.176-43.040 3.008-58.176 16.864-15.424 43.392-13.952 59.040 2.656l177.76 193.504 188.608-203.904c7.52-8 18.080-12.768 29.216-13.344 11.456-0.608 21.696 3.264 30.112 10.688 16.896 15.456 18.080 41.568 2.72 58.208z" />
<glyph unicode="&#xe90c;" glyph-name="icon-arrow-up" d="M757.216 356.928l-219.616 237.44c-8.128 8.576-19.296 13.632-31.040 13.632-11.744-0.288-23.2-5.056-31.040-13.664l-208.768-227.040c-15.36-16.928-14.176-43.040 3.008-58.176 16.864-15.424 43.392-13.952 59.040 2.656l177.76 193.504 188.608-203.904c7.52-8 18.080-12.768 29.216-13.344 11.456-0.608 21.696 3.264 30.112 10.688 16.896 15.456 18.080 41.568 2.72 58.208z" /> <glyph unicode="&#xe90c;" glyph-name="icon-arrow-right2" d="M420.928 202.784l237.44 219.616c8.576 8.128 13.632 19.296 13.632 31.040-0.288 11.744-5.056 23.2-13.664 31.040l-227.040 208.768c-16.928 15.36-43.040 14.176-58.176-3.008-15.424-16.864-13.952-43.392 2.656-59.040l193.504-177.76-203.904-188.608c-8-7.52-12.768-18.080-13.344-29.216-0.608-11.456 3.264-21.696 10.688-30.112 15.456-16.896 41.568-18.080 58.208-2.72z" />
<glyph unicode="&#xe90d;" glyph-name="icon-arrow-right2" d="M420.928 202.784l237.44 219.616c8.576 8.128 13.632 19.296 13.632 31.040-0.288 11.744-5.056 23.2-13.664 31.040l-227.040 208.768c-16.928 15.36-43.040 14.176-58.176-3.008-15.424-16.864-13.952-43.392 2.656-59.040l193.504-177.76-203.904-188.608c-8-7.52-12.768-18.080-13.344-29.216-0.608-11.456 3.264-21.696 10.688-30.112 15.456-16.896 41.568-18.080 58.208-2.72z" /> <glyph unicode="&#xe90d;" glyph-name="icon-plus" d="M768 448c0-22.080-17.92-40-40-40h-176v-176c0-22.080-17.92-40-40-40s-40 17.92-40 40v176h-176c-22.080 0-40 17.92-40 40s17.92 40 40 40h176v176c0 22.080 17.92 40 40 40s40-17.92 40-40v-176h176c22.080 0 40-17.92 40-40z" />
<glyph unicode="&#xe90e;" glyph-name="icon-plus" d="M768 448c0-22.080-17.92-40-40-40h-176v-176c0-22.080-17.92-40-40-40s-40 17.92-40 40v176h-176c-22.080 0-40 17.92-40 40s17.92 40 40 40h176v176c0 22.080 17.92 40 40 40s40-17.92 40-40v-176h176c22.080 0 40-17.92 40-40z" /> <glyph unicode="&#xe90e;" glyph-name="icon-arrow-right" d="M420.928 202.784l237.44 219.616c8.576 8.128 13.632 19.296 13.632 31.040-0.288 11.744-5.056 23.2-13.664 31.040l-227.040 208.768c-16.928 15.36-43.040 14.176-58.176-3.008-15.424-16.864-13.952-43.392 2.656-59.040l193.504-177.76-203.904-188.608c-8-7.52-12.768-18.080-13.344-29.216-0.608-11.456 3.264-21.696 10.688-30.112 15.456-16.896 41.568-18.080 58.208-2.72z" />
<glyph unicode="&#xe90f;" glyph-name="icon-help" d="M693.024 629.056c-99.968 99.936-262.080 99.936-362.048 0s-99.968-262.112 0-362.080c99.968-100 262.144-99.936 362.048 0 99.968 99.904 99.968 262.176 0 362.080zM501.216 242.048c-27.808 0-50.496 22.464-50.496 50.048 0 28.32 22.176 50.528 50.496 50.528 27.616 0 50.048-22.656 50.048-50.528 0.032-27.168-22.88-50.048-50.048-50.048zM536.416 417.536v-27.744c0-13.504-5.28-18.784-18.784-18.784h-36.224c-13.504 0-18.72 5.28-18.72 18.784v61.984c0 15.68 16.064 20.352 30.208 24.48 3.456 1.056 7.040 2.080 10.496 3.264 18.336 6.592 29.696 14.816 29.696 35.296 0 6.656 0 26.816-32.832 26.816-20.224 0-38.624-7.776-49.6-12.416-6.208-2.624-9.28-3.904-12.384-3.904-6.336 0-12.32 5.088-13.248 10.304l-12.608 32.96c-1.824 3.776-1.824 6.784-1.824 9.216 0 24.288 75.552 37.664 100.608 37.664 63.104 0 105.504-40.672 105.504-101.152 0.032-65.44-49.12-85.952-80.288-96.768z" /> <glyph unicode="&#xe90f;" glyph-name="icon-help" d="M693.024 629.056c-99.968 99.936-262.080 99.936-362.048 0s-99.968-262.112 0-362.080c99.968-100 262.144-99.936 362.048 0 99.968 99.904 99.968 262.176 0 362.080zM501.216 242.048c-27.808 0-50.496 22.464-50.496 50.048 0 28.32 22.176 50.528 50.496 50.528 27.616 0 50.048-22.656 50.048-50.528 0.032-27.168-22.88-50.048-50.048-50.048zM536.416 417.536v-27.744c0-13.504-5.28-18.784-18.784-18.784h-36.224c-13.504 0-18.72 5.28-18.72 18.784v61.984c0 15.68 16.064 20.352 30.208 24.48 3.456 1.056 7.040 2.080 10.496 3.264 18.336 6.592 29.696 14.816 29.696 35.296 0 6.656 0 26.816-32.832 26.816-20.224 0-38.624-7.776-49.6-12.416-6.208-2.624-9.28-3.904-12.384-3.904-6.336 0-12.32 5.088-13.248 10.304l-12.608 32.96c-1.824 3.776-1.824 6.784-1.824 9.216 0 24.288 75.552 37.664 100.608 37.664 63.104 0 105.504-40.672 105.504-101.152 0.032-65.44-49.12-85.952-80.288-96.768z" />
<glyph unicode="&#xe910;" glyph-name="icon-dashboard" d="M768 672v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32zM480 384v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32zM480 672v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32zM768 384v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32z" /> <glyph unicode="&#xe910;" glyph-name="icon-dashboard" d="M768 672v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32zM480 384v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32zM480 672v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32zM768 384v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32z" />
<glyph unicode="&#xe911;" glyph-name="icon-arrow-right" d="M420.928 202.784l237.44 219.616c8.576 8.128 13.632 19.296 13.632 31.040-0.288 11.744-5.056 23.2-13.664 31.040l-227.040 208.768c-16.928 15.36-43.040 14.176-58.176-3.008-15.424-16.864-13.952-43.392 2.656-59.040l193.504-177.76-203.904-188.608c-8-7.52-12.768-18.080-13.344-29.216-0.608-11.456 3.264-21.696 10.688-30.112 15.456-16.896 41.568-18.080 58.208-2.72z" /> <glyph unicode="&#xe911;" glyph-name="icon-eye-crossed" d="M768 456.64c-20.16-34.88-44.48-63.68-71.68-86.72l-64.64 64.64c0.64 4.16 0.96 8.64 0.96 12.8 0 60.16-51.84 108.8-115.84 108.8-2.24 0-4.16 0-6.4-0.32l-33.92 33.92c12.16 1.6 24 2.24 36.16 2.24 98.88 0 197.44-45.12 255.36-135.36zM348.8 646.080c-8.96 8.96-23.68 8.96-32.64 0l-3.52-3.52c-8.96-8.96-8.96-23.68 0-32.64l53.76-53.76c-43.52-22.4-81.6-56.32-110.4-100.8 83.84-130.56 226.88-177.6 348.48-137.28l57.6-57.6c9.28-9.28 23.68-9.28 32.96 0l3.2 3.2c8.96 8.96 8.96 23.68 0 32.64l-349.44 349.76zM516.8 338.56c-64-0.32-115.84 48.64-115.84 108.48-0.32 21.12 6.080 40.64 17.28 57.28l42.56-42.56c-1.92-5.76-2.56-12.16-1.92-18.88 2.88-30.080 30.72-52.16 62.72-49.92 2.24 0.32 4.8 0.64 7.040 0.96l42.56-42.56c-16.32-8.32-34.56-12.8-54.4-12.8z" />
<glyph unicode="&#xe912;" glyph-name="icon-eye-crossed" d="M768 456.64c-20.16-34.88-44.48-63.68-71.68-86.72l-64.64 64.64c0.64 4.16 0.96 8.64 0.96 12.8 0 60.16-51.84 108.8-115.84 108.8-2.24 0-4.16 0-6.4-0.32l-33.92 33.92c12.16 1.6 24 2.24 36.16 2.24 98.88 0 197.44-45.12 255.36-135.36zM348.8 646.080c-8.96 8.96-23.68 8.96-32.64 0l-3.52-3.52c-8.96-8.96-8.96-23.68 0-32.64l53.76-53.76c-43.52-22.4-81.6-56.32-110.4-100.8 83.84-130.56 226.88-177.6 348.48-137.28l57.6-57.6c9.28-9.28 23.68-9.28 32.96 0l3.2 3.2c8.96 8.96 8.96 23.68 0 32.64l-349.44 349.76zM516.8 338.56c-64-0.32-115.84 48.64-115.84 108.48-0.32 21.12 6.080 40.64 17.28 57.28l42.56-42.56c-1.92-5.76-2.56-12.16-1.92-18.88 2.88-30.080 30.72-52.16 62.72-49.92 2.24 0.32 4.8 0.64 7.040 0.96l42.56-42.56c-16.32-8.32-34.56-12.8-54.4-12.8z" /> <glyph unicode="&#xe912;" glyph-name="icon-T1" d="M603.2 694.4h-6.4c-25.494 5.341-54.79 8.398-84.8 8.398s-59.305-3.058-87.592-8.879l2.792 0.48h-6.72c-30.053-5.643-52.489-31.68-52.489-62.956 0-0.367 0.003-0.733 0.009-1.099l-0.001 0.055v-234.88c0.075-40.921 11.238-79.22 30.643-112.071l-0.563 1.031 35.2-60.48c11.655-19.297 32.515-32.001 56.342-32.001 0.105 0 0.209 0 0.314 0.001h44.144c0.359-0.007 0.783-0.011 1.208-0.011 23.569 0 44.162 12.74 55.269 31.709l0.164 0.302 36.16 64c18.232 31.447 29.027 69.173 29.12 109.413v232.987c0.005 0.293 0.008 0.639 0.008 0.986 0 31.391-22.599 57.503-52.416 62.954l-0.392 0.059zM629.76 396.8c-0.193-35.364-9.792-68.446-26.418-96.923l0.498 0.923-35.84-64c-6.868-11.865-19.463-19.742-33.906-19.84h-44.174c-0.073 0-0.159-0.001-0.246-0.001-14.427 0-27.041 7.762-33.894 19.338l-0.1 0.183-34.88 59.84c-16.656 28.155-26.515 62.042-26.56 98.227v235.853c0.133 19.025 13.742 34.833 31.751 38.359l0.249 0.041h6.72c24.050 5.126 51.682 8.062 80 8.062s55.949-2.936 82.608-8.519l-2.608 0.457h6.72c18.258-3.568 31.867-19.375 32-38.386v-0.014zM422.4 606.080h179.2c3.535 0 6.4-2.865 6.4-6.4v-99.2c0-3.535-2.865-6.4-6.4-6.4h-179.2c-3.535 0-6.4 2.865-6.4 6.4v99.2c0 3.535 2.865 6.4 6.4 6.4z" />
<glyph unicode="&#xe913;" glyph-name="icon-T1" d="M603.2 694.4h-6.4c-25.494 5.341-54.79 8.398-84.8 8.398s-59.305-3.058-87.592-8.879l2.792 0.48h-6.72c-30.053-5.643-52.489-31.68-52.489-62.956 0-0.367 0.003-0.733 0.009-1.099l-0.001 0.055v-234.88c0.075-40.921 11.238-79.22 30.643-112.071l-0.563 1.031 35.2-60.48c11.655-19.297 32.515-32.001 56.342-32.001 0.105 0 0.209 0 0.314 0.001h44.144c0.359-0.007 0.783-0.011 1.208-0.011 23.569 0 44.162 12.74 55.269 31.709l0.164 0.302 36.16 64c18.232 31.447 29.027 69.173 29.12 109.413v232.987c0.005 0.293 0.008 0.639 0.008 0.986 0 31.391-22.599 57.503-52.416 62.954l-0.392 0.059zM629.76 396.8c-0.193-35.364-9.792-68.446-26.418-96.923l0.498 0.923-35.84-64c-6.868-11.865-19.463-19.742-33.906-19.84h-44.174c-0.073 0-0.159-0.001-0.246-0.001-14.427 0-27.041 7.762-33.894 19.338l-0.1 0.183-34.88 59.84c-16.656 28.155-26.515 62.042-26.56 98.227v235.853c0.133 19.025 13.742 34.833 31.751 38.359l0.249 0.041h6.72c24.050 5.126 51.682 8.062 80 8.062s55.949-2.936 82.608-8.519l-2.608 0.457h6.72c18.258-3.568 31.867-19.375 32-38.386v-0.014zM422.4 606.080h179.2c3.535 0 6.4-2.865 6.4-6.4v-99.2c0-3.535-2.865-6.4-6.4-6.4h-179.2c-3.535 0-6.4 2.865-6.4 6.4v99.2c0 3.535 2.865 6.4 6.4 6.4z" /> <glyph unicode="&#xe913;" glyph-name="icon-T1-buttons" d="M603.2 694.4h-6.4c-25.494 5.341-54.79 8.398-84.8 8.398s-59.305-3.058-87.592-8.879l2.792 0.48h-6.72c-30.053-5.643-52.489-31.68-52.489-62.956 0-0.367 0.003-0.733 0.009-1.099l-0.001 0.055v-234.88c0.075-40.921 11.238-79.22 30.643-112.071l-0.563 1.031 35.2-60.48c11.655-19.297 32.515-32.001 56.342-32.001 0.105 0 0.209 0 0.314 0.001h44.144c0.359-0.007 0.783-0.011 1.208-0.011 23.569 0 44.162 12.74 55.269 31.709l0.164 0.302 36.16 64c18.152 31.468 28.933 69.175 29.12 109.385v233.015c0.005 0.293 0.008 0.639 0.008 0.986 0 31.391-22.599 57.503-52.416 62.954l-0.392 0.059zM629.76 396.8c-0.193-35.364-9.792-68.446-26.418-96.923l0.498 0.923-35.84-64c-6.868-11.865-19.463-19.742-33.906-19.84h-44.174c-14.469 0.112-27.111 7.827-34.139 19.343l-34.981 61.297c-16.687 28.041-26.553 61.827-26.56 97.918v234.882c0 19.072 13.676 34.95 31.757 38.362l0.243 0.038h6.72c24.050 5.126 51.682 8.062 80 8.062s55.949-2.936 82.608-8.519l-2.608 0.457h6.72c18.324-3.45 32-19.328 32-38.4v0zM422.4 606.080h179.2c3.535 0 6.4-2.865 6.4-6.4v-99.2c0-3.535-2.865-6.4-6.4-6.4h-179.2c-3.535 0-6.4 2.865-6.4 6.4v99.2c0 3.535 2.865 6.4 6.4 6.4zM531.2 469.76h70.4c3.535 0 6.4-2.865 6.4-6.4v-28.8c0-3.535-2.865-6.4-6.4-6.4h-70.4c-3.535 0-6.4 2.865-6.4 6.4v28.8c0 3.535 2.865 6.4 6.4 6.4zM422.4 468.8h70.4c3.535 0 6.4-2.865 6.4-6.4v-28.8c0-3.535-2.865-6.4-6.4-6.4h-70.4c-3.535 0-6.4 2.865-6.4 6.4v28.8c0 3.535 2.865 6.4 6.4 6.4z" />
<glyph unicode="&#xe914;" glyph-name="icon-T1-buttons" d="M603.2 694.4h-6.4c-25.494 5.341-54.79 8.398-84.8 8.398s-59.305-3.058-87.592-8.879l2.792 0.48h-6.72c-30.053-5.643-52.489-31.68-52.489-62.956 0-0.367 0.003-0.733 0.009-1.099l-0.001 0.055v-234.88c0.075-40.921 11.238-79.22 30.643-112.071l-0.563 1.031 35.2-60.48c11.655-19.297 32.515-32.001 56.342-32.001 0.105 0 0.209 0 0.314 0.001h44.144c0.359-0.007 0.783-0.011 1.208-0.011 23.569 0 44.162 12.74 55.269 31.709l0.164 0.302 36.16 64c18.152 31.468 28.933 69.175 29.12 109.385v233.015c0.005 0.293 0.008 0.639 0.008 0.986 0 31.391-22.599 57.503-52.416 62.954l-0.392 0.059zM629.76 396.8c-0.193-35.364-9.792-68.446-26.418-96.923l0.498 0.923-35.84-64c-6.868-11.865-19.463-19.742-33.906-19.84h-44.174c-14.469 0.112-27.111 7.827-34.139 19.343l-34.981 61.297c-16.687 28.041-26.553 61.827-26.56 97.918v234.882c0 19.072 13.676 34.95 31.757 38.362l0.243 0.038h6.72c24.050 5.126 51.682 8.062 80 8.062s55.949-2.936 82.608-8.519l-2.608 0.457h6.72c18.324-3.45 32-19.328 32-38.4v0zM422.4 606.080h179.2c3.535 0 6.4-2.865 6.4-6.4v-99.2c0-3.535-2.865-6.4-6.4-6.4h-179.2c-3.535 0-6.4 2.865-6.4 6.4v99.2c0 3.535 2.865 6.4 6.4 6.4zM531.2 469.76h70.4c3.535 0 6.4-2.865 6.4-6.4v-28.8c0-3.535-2.865-6.4-6.4-6.4h-70.4c-3.535 0-6.4 2.865-6.4 6.4v28.8c0 3.535 2.865 6.4 6.4 6.4zM422.4 468.8h70.4c3.535 0 6.4-2.865 6.4-6.4v-28.8c0-3.535-2.865-6.4-6.4-6.4h-70.4c-3.535 0-6.4 2.865-6.4 6.4v28.8c0 3.535 2.865 6.4 6.4 6.4z" /> <glyph unicode="&#xe914;" glyph-name="icon-T1-connect" d="M526.72 345.92h-29.44c-16.229 0.18-30.307 9.217-37.647 22.496l-0.113 0.224-23.36 41.92c-12.685 22.404-20.16 49.203-20.16 77.748 0 0.004 0 0.008 0 0.012v-0.001 164.16c-0.002 0.138-0.002 0.301-0.002 0.463 0 21.403 14.903 39.326 34.898 43.957l0.304 0.059h4.48c16.978 3.729 36.479 5.865 56.48 5.865s39.502-2.136 58.289-6.193l-1.809 0.327h4.48c20.14-4.81 34.883-22.655 34.883-43.943 0-0.189-0.001-0.377-0.003-0.565v0.029-163.52c0-0.064 0-0.139 0-0.214 0-28.079-7.233-54.468-19.937-77.407l0.417 0.821-24-43.84c-7.529-13.375-21.591-22.288-37.744-22.4h-0.016zM512 685.76c-0.097 0-0.212 0-0.327 0-18.693 0-36.923-1.981-54.492-5.745l1.699 0.305h-4.48c-12.188-2.865-21.122-13.645-21.122-26.512 0-0.129 0.001-0.258 0.003-0.387v0.020-164.48c0-0.094 0-0.204 0-0.315 0-25.018 6.525-48.512 17.966-68.873l-0.365 0.708 23.36-41.92c4.497-8.071 12.981-13.44 22.718-13.44 0.001 0 0.002 0 0.002 0h29.44c9.799 0.067 18.299 5.56 22.652 13.623l0.068 0.137 24 43.84c10.854 19.495 17.254 42.757 17.28 67.512v162.248c0.002 0.109 0.002 0.238 0.002 0.368 0 12.867-8.935 23.648-20.938 26.476l-0.184 0.037h-4.48c-15.736 3.739-33.906 6.053-52.557 6.396l-0.243 0.004zM452.48 635.52h119.040c2.474 0 4.48-2.006 4.48-4.48v-69.44c0-2.474-2.006-4.48-4.48-4.48h-119.040c-2.474 0-4.48 2.006-4.48 4.48v69.44c0 2.474 2.006 4.48 4.48 4.48zM540.48 303.68v-52.16h-18.24v-59.52h-19.84v59.52h-18.88v52.16h56.96z" />
<glyph unicode="&#xe915;" glyph-name="icon-T1-connect" d="M526.72 345.92h-29.44c-16.229 0.18-30.307 9.217-37.647 22.496l-0.113 0.224-23.36 41.92c-12.685 22.404-20.16 49.203-20.16 77.748 0 0.004 0 0.008 0 0.012v-0.001 164.16c-0.002 0.138-0.002 0.301-0.002 0.463 0 21.403 14.903 39.326 34.898 43.957l0.304 0.059h4.48c16.978 3.729 36.479 5.865 56.48 5.865s39.502-2.136 58.289-6.193l-1.809 0.327h4.48c20.14-4.81 34.883-22.655 34.883-43.943 0-0.189-0.001-0.377-0.003-0.565v0.029-163.52c0-0.064 0-0.139 0-0.214 0-28.079-7.233-54.468-19.937-77.407l0.417 0.821-24-43.84c-7.529-13.375-21.591-22.288-37.744-22.4h-0.016zM512 685.76c-0.097 0-0.212 0-0.327 0-18.693 0-36.923-1.981-54.492-5.745l1.699 0.305h-4.48c-12.188-2.865-21.122-13.645-21.122-26.512 0-0.129 0.001-0.258 0.003-0.387v0.020-164.48c0-0.094 0-0.204 0-0.315 0-25.018 6.525-48.512 17.966-68.873l-0.365 0.708 23.36-41.92c4.497-8.071 12.981-13.44 22.718-13.44 0.001 0 0.002 0 0.002 0h29.44c9.799 0.067 18.299 5.56 22.652 13.623l0.068 0.137 24 43.84c10.854 19.495 17.254 42.757 17.28 67.512v162.248c0.002 0.109 0.002 0.238 0.002 0.368 0 12.867-8.935 23.648-20.938 26.476l-0.184 0.037h-4.48c-15.736 3.739-33.906 6.053-52.557 6.396l-0.243 0.004zM452.48 635.52h119.040c2.474 0 4.48-2.006 4.48-4.48v-69.44c0-2.474-2.006-4.48-4.48-4.48h-119.040c-2.474 0-4.48 2.006-4.48 4.48v69.44c0 2.474 2.006 4.48 4.48 4.48zM540.48 303.68v-52.16h-18.24v-59.52h-19.84v59.52h-18.88v52.16h56.96z" /> <glyph unicode="&#xe915;" d="M551.36 178.24h-78.4c-0.225-0.006-0.489-0.010-0.754-0.010-10.242 0-19.207 5.468-24.135 13.643l-0.071 0.127-64 96c-29.426 43.619-46.99 97.353-47.040 155.187v233.613c0.36 22.875 18.988 41.281 41.915 41.281 0.114 0 0.228 0 0.342-0.001h263.342c0.097 0.001 0.211 0.001 0.325 0.001 22.927 0 41.555-18.406 41.915-41.248v-239.394c-0.289-59.994-19.423-115.463-51.779-160.855l0.579 0.855-60.48-86.080c-4.695-7.399-12.575-12.414-21.664-13.114l-0.096-0.006zM380.48 690.24c-0.1 0.003-0.217 0.004-0.334 0.004-7.552 0-13.716-5.946-14.064-13.413l-0.001-0.031v-233.92c0-0.010 0-0.023 0-0.036 0-52.093 15.777-100.502 42.813-140.708l-0.573 0.904 64-96h79.040l60.16 86.080c28.821 40.408 46.080 90.794 46.080 145.211 0 0.024 0 0.049 0 0.073v-0.004 238.4c-0.35 7.498-6.513 13.444-14.066 13.444-0.118 0-0.235-0.001-0.352-0.004h0.017z" />
<glyph unicode="&#xe916;" d="M551.36 178.24h-78.4c-0.225-0.006-0.489-0.010-0.754-0.010-10.242 0-19.207 5.468-24.135 13.643l-0.071 0.127-64 96c-29.426 43.619-46.99 97.353-47.040 155.187v233.613c0.36 22.875 18.988 41.281 41.915 41.281 0.114 0 0.228 0 0.342-0.001h263.342c0.097 0.001 0.211 0.001 0.325 0.001 22.927 0 41.555-18.406 41.915-41.248v-239.394c-0.289-59.994-19.423-115.463-51.779-160.855l0.579 0.855-60.48-86.080c-4.695-7.399-12.575-12.414-21.664-13.114l-0.096-0.006zM380.48 690.24c-0.1 0.003-0.217 0.004-0.334 0.004-7.552 0-13.716-5.946-14.064-13.413l-0.001-0.031v-233.92c0-0.010 0-0.023 0-0.036 0-52.093 15.777-100.502 42.813-140.708l-0.573 0.904 64-96h79.040l60.16 86.080c28.821 40.408 46.080 90.794 46.080 145.211 0 0.024 0 0.049 0 0.073v-0.004 238.4c-0.35 7.498-6.513 13.444-14.066 13.444-0.118 0-0.235-0.001-0.352-0.004h0.017z" /> <glyph unicode="&#xe916;" d="M407.040 624.32h209.92c4.595 0 8.32-3.725 8.32-8.32v-201.92c0-4.595-3.725-8.32-8.32-8.32h-209.92c-4.595 0-8.32 3.725-8.32 8.32v201.92c0 4.595 3.725 8.32 8.32 8.32z" />
<glyph unicode="&#xe917;" d="M407.040 624.32h209.92c4.595 0 8.32-3.725 8.32-8.32v-201.92c0-4.595-3.725-8.32-8.32-8.32h-209.92c-4.595 0-8.32 3.725-8.32 8.32v201.92c0 4.595 3.725 8.32 8.32 8.32z" /> <glyph unicode="&#xe917;" d="M539.52 352h-54.72c-6.578 0.052-12.387 3.298-15.96 8.261l-0.040 0.059-45.12 64c-20.029 28.726-32.002 64.366-32.002 102.802 0 0.309 0.001 0.617 0.002 0.925v-0.047 157.76c0.349 15.459 12.963 27.856 28.473 27.856 0.34 0 0.679-0.006 1.016-0.018l-0.049 0.001h184.32c0.288 0.010 0.627 0.016 0.967 0.016 15.51 0 28.124-12.398 28.473-27.824l0.001-0.032v-160c-0.074-40.641-13.522-78.128-36.176-108.308l0.336 0.468-42.24-57.28c-3.622-5.258-9.609-8.66-16.39-8.66-0.313 0-0.624 0.007-0.934 0.022l0.044-0.002zM419.84 694.4c-0.205 0.016-0.444 0.025-0.686 0.025-4.973 0-9.062-3.781-9.551-8.624l-0.003-0.040v-155.84c0.066-34.769 11.081-66.953 29.778-93.302l-0.338 0.502 45.12-64h56l42.24 57.28c19.839 26.468 31.828 59.817 32 95.96v160.040c-0.492 4.884-4.582 8.665-9.554 8.665-0.241 0-0.481-0.009-0.717-0.026l0.032 0.002z" />
<glyph unicode="&#xe918;" d="M539.52 352h-54.72c-6.578 0.052-12.387 3.298-15.96 8.261l-0.040 0.059-45.12 64c-20.029 28.726-32.002 64.366-32.002 102.802 0 0.309 0.001 0.617 0.002 0.925v-0.047 157.76c0.349 15.459 12.963 27.856 28.473 27.856 0.34 0 0.679-0.006 1.016-0.018l-0.049 0.001h184.32c0.288 0.010 0.627 0.016 0.967 0.016 15.51 0 28.124-12.398 28.473-27.824l0.001-0.032v-160c-0.074-40.641-13.522-78.128-36.176-108.308l0.336 0.468-42.24-57.28c-3.622-5.258-9.609-8.66-16.39-8.66-0.313 0-0.624 0.007-0.934 0.022l0.044-0.002zM419.84 694.4c-0.205 0.016-0.444 0.025-0.686 0.025-4.973 0-9.062-3.781-9.551-8.624l-0.003-0.040v-155.84c0.066-34.769 11.081-66.953 29.778-93.302l-0.338 0.502 45.12-64h56l42.24 57.28c19.839 26.468 31.828 59.817 32 95.96v160.040c-0.492 4.884-4.582 8.665-9.554 8.665-0.241 0-0.481-0.009-0.717-0.026l0.032 0.002z" /> <glyph unicode="&#xe918;" d="M438.4 650.88h146.88c3.181 0 5.76-2.579 5.76-5.76v-134.4c0-3.181-2.579-5.76-5.76-5.76h-146.88c-3.181 0-5.76 2.579-5.76 5.76v134.4c0 3.181 2.579 5.76 5.76 5.76z" />
<glyph unicode="&#xe919;" d="M438.4 650.88h146.88c3.181 0 5.76-2.579 5.76-5.76v-134.4c0-3.181-2.579-5.76-5.76-5.76h-146.88c-3.181 0-5.76 2.579-5.76 5.76v134.4c0 3.181 2.579 5.76 5.76 5.76z" /> <glyph unicode="&#xe919;" d="M541.12 300.48v-50.56h-18.56v-57.92h-20.48v57.92h-18.88v50.56h57.92z" />
<glyph unicode="&#xe91a;" d="M541.12 300.48v-50.56h-18.56v-57.92h-20.48v57.92h-18.88v50.56h57.92z" /> <glyph unicode="&#xe91a;" glyph-name="icon-arrow-left" d="M603.072 202.784l-237.44 219.616c-8.576 8.128-13.632 19.296-13.632 31.040 0.288 11.744 5.056 23.2 13.664 31.040l227.040 208.768c16.928 15.36 43.040 14.176 58.176-3.008 15.424-16.864 13.952-43.392-2.656-59.040l-193.504-177.76 203.904-188.608c8-7.52 12.768-18.080 13.344-29.216 0.608-11.456-3.264-21.696-10.688-30.112-15.456-16.896-41.568-18.080-58.208-2.72z" />
<glyph unicode="&#xe91b;" glyph-name="icon-top" d="M677.44 346.24c-3.255-1.423-7.047-2.252-11.033-2.252-0.284 0-0.566 0.004-0.848 0.013l0.041-0.001c-8.323 0.531-15.657 4.371-20.77 10.206l-0.030 0.034-93.44 109.44c-0.378 0.735-1.131 1.229-1.999 1.229-1.237 0-2.24-1.003-2.24-2.24 0-0.209 0.029-0.412 0.083-0.605l-0.004 0.016v-233.28c0.102-0.987 0.16-2.132 0.16-3.291 0-18.733-15.187-33.92-33.92-33.92s-33.92 15.187-33.92 33.92c0 1.159 0.058 2.304 0.172 3.433l-0.012-0.142v236.16c0.050 0.177 0.079 0.379 0.079 0.589 0 1.237-1.003 2.24-2.24 2.24-0.868 0-1.621-0.494-1.993-1.216l-0.006-0.013-88.32-104.32c-5.204-6.343-13.042-10.358-21.819-10.358-7.711 0-14.699 3.099-19.784 8.121l0.003-0.003c-6.16 5.845-9.993 14.090-9.993 23.231 0 8.17 3.062 15.625 8.101 21.28l-0.028-0.032 146.56 173.44c5.311 6.15 13.061 10.069 21.731 10.24h0.029c8.727-0.036 16.523-3.991 21.724-10.196l0.036-0.044 152-178.56c5.441-6.124 8.764-14.234 8.764-23.121 0-12.698-6.785-23.81-16.927-29.911l-0.157-0.088zM329.28 667.2c-0.024 0.488-0.038 1.060-0.038 1.635 0 18.891 14.881 34.306 33.561 35.163l0.077 0.003h292.48c18.795-1.81 33.372-17.523 33.372-36.64s-14.577-34.83-33.222-36.628l-0.15-0.012h-292.48c-18.751 0.866-33.625 16.278-33.625 35.165 0 0.463 0.009 0.923 0.027 1.381l-0.002-0.066z" /> <glyph unicode="&#xe91b;" glyph-name="icon-top" d="M677.44 346.24c-3.255-1.423-7.047-2.252-11.033-2.252-0.284 0-0.566 0.004-0.848 0.013l0.041-0.001c-8.323 0.531-15.657 4.371-20.77 10.206l-0.030 0.034-93.44 109.44c-0.378 0.735-1.131 1.229-1.999 1.229-1.237 0-2.24-1.003-2.24-2.24 0-0.209 0.029-0.412 0.083-0.605l-0.004 0.016v-233.28c0.102-0.987 0.16-2.132 0.16-3.291 0-18.733-15.187-33.92-33.92-33.92s-33.92 15.187-33.92 33.92c0 1.159 0.058 2.304 0.172 3.433l-0.012-0.142v236.16c0.050 0.177 0.079 0.379 0.079 0.589 0 1.237-1.003 2.24-2.24 2.24-0.868 0-1.621-0.494-1.993-1.216l-0.006-0.013-88.32-104.32c-5.204-6.343-13.042-10.358-21.819-10.358-7.711 0-14.699 3.099-19.784 8.121l0.003-0.003c-6.16 5.845-9.993 14.090-9.993 23.231 0 8.17 3.062 15.625 8.101 21.28l-0.028-0.032 146.56 173.44c5.311 6.15 13.061 10.069 21.731 10.24h0.029c8.727-0.036 16.523-3.991 21.724-10.196l0.036-0.044 152-178.56c5.441-6.124 8.764-14.234 8.764-23.121 0-12.698-6.785-23.81-16.927-29.911l-0.157-0.088zM329.28 667.2c-0.024 0.488-0.038 1.060-0.038 1.635 0 18.891 14.881 34.306 33.561 35.163l0.077 0.003h292.48c18.795-1.81 33.372-17.523 33.372-36.64s-14.577-34.83-33.222-36.628l-0.15-0.012h-292.48c-18.751 0.866-33.625 16.278-33.625 35.165 0 0.463 0.009 0.923 0.027 1.381l-0.002-0.066z" />
<glyph unicode="&#xe91c;" glyph-name="icon-check" d="M692.8 646.080l-1.92 1.92c-6.246 7.057-15.326 11.484-25.44 11.484s-19.194-4.427-25.409-11.448l-0.031-0.036-196.48-224-3.84-1.6-3.84 1.92-48.64 57.28c-7.010 7.905-17.193 12.862-28.533 12.862-21.031 0-38.080-17.049-38.080-38.080 0-7.495 2.165-14.485 5.905-20.377l-0.092 0.155 100.8-148.16c5.391-8.036 14.386-13.292 24.618-13.44h8.662c17.251 0.146 32.385 9.075 41.163 22.529l0.117 0.191 195.2 296.32c4.473 6.632 7.141 14.803 7.141 23.597 0 11.162-4.297 21.32-11.326 28.911l0.025-0.028z" /> <glyph unicode="&#xe91c;" glyph-name="icon-check" d="M692.8 646.080l-1.92 1.92c-6.246 7.057-15.326 11.484-25.44 11.484s-19.194-4.427-25.409-11.448l-0.031-0.036-196.48-224-3.84-1.6-3.84 1.92-48.64 57.28c-7.010 7.905-17.193 12.862-28.533 12.862-21.031 0-38.080-17.049-38.080-38.080 0-7.495 2.165-14.485 5.905-20.377l-0.092 0.155 100.8-148.16c5.391-8.036 14.386-13.292 24.618-13.44h8.662c17.251 0.146 32.385 9.075 41.163 22.529l0.117 0.191 195.2 296.32c4.473 6.632 7.141 14.803 7.141 23.597 0 11.162-4.297 21.32-11.326 28.911l0.025-0.028z" />
<glyph unicode="&#xe91d;" glyph-name="icon-error" d="M693.12 629.12c-46.317 46.267-110.276 74.88-180.919 74.88-141.385 0-256-114.615-256-256s114.615-256 256-256c70.642 0 134.602 28.613 180.921 74.882l-0.002-0.002c46.387 46.337 75.081 110.377 75.081 181.12s-28.694 134.783-75.079 181.118l-0.002 0.002zM494.080 615.68h53.12c16 0 18.24-9.28 18.24-14.72v-10.24l-10.88-194.56c0-14.4-8-17.28-18.88-17.28h-28.16c-10.56 0-17.28 2.88-18.88 17.92l-10.88 193.92v10.56c-1.28 4.8 2.24 14.080 16.32 14.080zM521.28 242.24c-0.095-0.001-0.207-0.001-0.319-0.001-27.747 0-50.24 22.493-50.24 50.24s22.493 50.24 50.24 50.24c27.747 0 50.24-22.493 50.24-50.24 0-0.112 0-0.224-0.001-0.336v0.017c0 0 0-0.001 0-0.001 0-27.634-22.311-50.057-49.903-50.239h-0.017z" /> <glyph unicode="&#xe91d;" glyph-name="icon-error" d="M693.12 629.12c-46.317 46.267-110.276 74.88-180.919 74.88-141.385 0-256-114.615-256-256s114.615-256 256-256c70.642 0 134.602 28.613 180.921 74.882l-0.002-0.002c46.387 46.337 75.081 110.377 75.081 181.12s-28.694 134.783-75.079 181.118l-0.002 0.002zM494.080 615.68h53.12c16 0 18.24-9.28 18.24-14.72v-10.24l-10.88-194.56c0-14.4-8-17.28-18.88-17.28h-28.16c-10.56 0-17.28 2.88-18.88 17.92l-10.88 193.92v10.56c-1.28 4.8 2.24 14.080 16.32 14.080zM521.28 242.24c-0.095-0.001-0.207-0.001-0.319-0.001-27.747 0-50.24 22.493-50.24 50.24s22.493 50.24 50.24 50.24c27.747 0 50.24-22.493 50.24-50.24 0-0.112 0-0.224-0.001-0.336v0.017c0 0 0-0.001 0-0.001 0-27.634-22.311-50.057-49.903-50.239h-0.017z" />
<glyph unicode="&#xe91e;" glyph-name="icon-eye" d="M512.64 592c-99.2 0-198.4-45.76-256.64-136.64 128.64-200 394.56-203.84 512 1.28-57.92 90.24-156.48 135.36-255.36 135.36zM516.8 338.56c-64-0.32-115.84 48.64-115.84 108.48-0.32 60.16 51.52 109.12 115.84 109.12 64 0 115.84-48.64 115.84-108.8 0.32-60.16-51.52-108.8-115.84-108.8zM574.72 451.84c2.56-30.080-21.12-56.32-53.12-58.88-32-2.24-59.84 19.84-62.72 49.92-2.56 30.080 21.44 56.32 53.12 58.56 32 2.56 59.84-19.84 62.72-49.6z" /> <glyph unicode="&#xe91e;" glyph-name="icon-eye" d="M512.64 592c-99.2 0-198.4-45.76-256.64-136.64 128.64-200 394.56-203.84 512 1.28-57.92 90.24-156.48 135.36-255.36 135.36zM516.8 338.56c-64-0.32-115.84 48.64-115.84 108.48-0.32 60.16 51.52 109.12 115.84 109.12 64 0 115.84-48.64 115.84-108.8 0.32-60.16-51.52-108.8-115.84-108.8zM574.72 451.84c2.56-30.080-21.12-56.32-53.12-58.88-32-2.24-59.84 19.84-62.72 49.92-2.56 30.080 21.44 56.32 53.12 58.56 32 2.56 59.84-19.84 62.72-49.6z" />
<glyph unicode="&#xe91f;" glyph-name="icon-back" d="M656.224 557.696l-66.848-66.176-66.848 66.176-50.144-49.6 66.912-66.176-66.912-66.176 50.176-49.632 66.848 66.176 66.848-66.176 50.112 49.632-66.816 66.176 66.816 66.176-50.144 49.6zM337.824 704h540.928c27.2 0 49.248-21.824 49.248-48.768v-414.464c0-26.944-22.048-48.768-49.248-48.768h-540.608c-13.856 0-27.072 5.792-36.416 15.936l-192.896 209.664c-17.248 18.752-17.088 47.488 0.352 66.048l192.576 204.8c9.344 9.92 22.4 15.552 36.064 15.552z" />
</font></defs></svg> </font></defs></svg>

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/images/T.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 557 KiB

View File

@ -1,90 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Vrstva_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 211 61" style="enable-background:new 0 0 211 61;" xml:space="preserve">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;}
</style>
<path id="unlocked" class="st0" d="M12,15v-2.6c0-2.1,1.7-3.8,3.7-3.8h1.1c2,0,3.7,1.7,3.7,3.8v0.2h-2.1c-0.1-1.1-1-1.9-2.1-1.9
c-1.2,0-2.1,1-2.1,2.2V15h6.8c0.6,0,1,0.5,1,1v6.5c0,0.6-0.5,1-1,1h-9.8c-0.6,0-1-0.5-1-1V16c0-0.6,0.5-1,1-1H12z"/>
<path id="locked" class="st0" d="M50.9,15h-0.5v-2.6c0-2.1-1.7-3.8-3.7-3.8h-1.1c-2,0-3.7,1.7-3.7,3.8V15h-0.9c-0.6,0-1,0.5-1,1v6.5
c0,0.6,0.5,1,1,1h9.8c0.6,0,1-0.5,1-1V16C51.9,15.4,51.5,15,50.9,15z M44.1,12.8c0-1.2,0.9-2.2,2.1-2.2c1.1,0,2,1,2.1,2.1V15h-4.2
V12.8z"/>
<g id="refresh">
<path d="M101,14.9c0.5-2.4,2.7-4.2,5.3-4.2c1,0,1.9,0.3,2.7,0.7c0.2,0.1,0.4,0.3,0.6,0.4l-2.1,2l5.8,1l-1-5.8l-1.2,1.2
c-0.2-0.1-0.4-0.3-0.6-0.4c-1.2-0.8-2.7-1.3-4.3-1.3c-3.7,0-6.8,2.6-7.4,6.2l-0.1,0.6h2.2L101,14.9z"/>
<path d="M111,17.2c-0.5,2.4-2.7,4.2-5.3,4.2c-1,0-1.9-0.3-2.7-0.7c-0.2-0.1-0.4-0.3-0.6-0.4l2.1-2l-5.8-1l1,5.8l1.2-1.2
c0.2,0.1,0.4,0.3,0.6,0.4c1.2,0.8,2.7,1.3,4.3,1.3c3.7,0,6.8-2.6,7.4-6.2l0.1-0.6h-2.2L111,17.2z"/>
</g>
<path id="settings" d="M142.7,14.5h-2.1c-0.1-0.4-0.3-0.8-0.5-1.1l1.4-1.4c0.3-0.3,0.3-0.9,0-1.2l-0.6-0.6c-0.3-0.3-0.9-0.3-1.2,0
l-1.4,1.4c-0.4-0.2-0.8-0.4-1.2-0.5V9.3c0-0.5-0.4-0.8-0.8-0.8h-0.8c-0.5,0-0.8,0.4-0.8,0.8v1.8c-0.5,0.1-0.9,0.3-1.3,0.6l-1.2-1.2
c-0.3-0.3-0.9-0.3-1.2,0l-0.6,0.6c-0.3,0.3-0.3,0.9,0,1.2l1.2,1.2c-0.3,0.4-0.4,0.9-0.6,1.4h-1.6c-0.5,0-0.8,0.4-0.8,0.8v0.8
c0,0.5,0.4,0.8,0.8,0.8h1.7c0.2,0.5,0.4,1,0.7,1.4l-1.1,1.1c-0.3,0.3-0.3,0.9,0,1.2l0.6,0.6c0.3,0.3,0.9,0.3,1.2,0l1.3-1.3
c0.4,0.2,0.9,0.4,1.4,0.4v1.7c0,0.5,0.4,0.8,0.8,0.8h0.8c0.5,0,0.8-0.4,0.8-0.8v-2c0.4-0.1,0.8-0.3,1.2-0.6l1.3,1.3
c0.3,0.3,0.9,0.3,1.2,0l0.6-0.6c0.3-0.3,0.3-0.9,0-1.2l-1.5-1.5c0.2-0.4,0.3-0.8,0.4-1.2h2c0.5,0,0.8-0.4,0.8-0.8v-0.8
C143.5,14.9,143.1,14.5,142.7,14.5z M135.8,18.1c-1.2,0-2.1-0.9-2.1-2.1c0-1.2,0.9-2.1,2.1-2.1c1.2,0,2.1,0.9,2.1,2.1
C137.9,17.2,136.9,18.1,135.8,18.1z"/>
<g id="eject">
<path d="M69.5,22.4h12.9c0.3,0,0.5-0.2,0.5-0.5V20c0-0.3-0.2-0.5-0.5-0.5H69.5c-0.3,0-0.5,0.2-0.5,0.5v1.9
C69,22.2,69.2,22.4,69.5,22.4z"/>
<path d="M75.8,9.7l-6.3,7.2c-0.2,0.2,0,0.5,0.2,0.5h12.6c0.3,0,0.4-0.3,0.2-0.5l-6.3-7.2C76.1,9.5,75.9,9.5,75.8,9.7z"/>
</g>
<path id="redirect" d="M76,38.5c-4.1,0-7.5,3.4-7.5,7.5c0,4.1,3.4,7.5,7.5,7.5s7.5-3.4,7.5-7.5C83.5,41.9,80.1,38.5,76,38.5z
M76.5,49.5v-2c-3-1-4,0-5,2c0-5,3-6,5-6v-2l4,4L76.5,49.5z"/>
<path id="warning" d="M113.5,51.9l-7-12.3c-0.3-0.5-0.7-0.5-1,0l-7,12.3c-0.3,0.5,0,0.9,0.5,0.9h14
C113.6,52.7,113.8,52.4,113.5,51.9z M107,50.2c0,0.3-0.2,0.5-0.5,0.5h-1c-0.3,0-0.5-0.2-0.5-0.5v-1c0-0.3,0.2-0.5,0.5-0.5h1
c0.3,0,0.5,0.2,0.5,0.5V50.2z M107,47.2c0,0.3-0.2,0.5-0.5,0.5h-1c-0.3,0-0.5-0.2-0.5-0.5v-3c0-0.3,0.2-0.5,0.5-0.5h1
c0.3,0,0.5,0.2,0.5,0.5V47.2z"/>
<path id="chat" d="M48,38.5h-4c-3,0-5.5,2.5-5.5,5.6c0,2.8,2,5.2,4.7,5.6v3.8l3.8-3.8H48c3,0,5.5-2.5,5.5-5.6
C53.5,41,51,38.5,48,38.5z"/>
<path id="info" d="M21.3,40.7c-2.9-2.9-7.7-2.9-10.6,0c-2.9,2.9-2.9,7.7,0,10.6c2.9,2.9,7.7,2.9,10.6,0
C24.2,48.4,24.2,43.6,21.3,40.7z M15.9,39.8c0.8,0,1.4,0.6,1.4,1.4c0,0.8-0.6,1.4-1.4,1.4c-0.8,0-1.5-0.7-1.5-1.4
C14.4,40.4,15.1,39.8,15.9,39.8z M18.2,50.4c0,0.3-0.1,0.4-0.4,0.4H14c-0.3,0-0.4-0.1-0.4-0.4v-0.9c0-0.3,0.1-0.4,0.4-0.4h0.7v-4H14
c-0.3,0-0.4-0.1-0.4-0.4v-0.9c0-0.3,0.1-0.4,0.4-0.4h2.6c0.3,0,0.4,0.1,0.4,0.4V49h0.7c0.3,0,0.4,0.1,0.4,0.4V50.4z"/>
<g id="close">
<g>
<path d="M143.1,51.2c0.5,0.5,0.5,1.4,0,1.9c-0.3,0.3-0.6,0.4-1,0.4c-0.4,0-0.7-0.1-0.9-0.4l-5.2-5.2l-5.2,5.2
c-0.5,0.5-1.4,0.5-1.9,0c-0.5-0.5-0.5-1.4,0-1.9l5.2-5.2l-5.2-5.2c-0.5-0.5-0.5-1.4,0-1.9c0.5-0.5,1.4-0.5,1.9,0l5.2,5.2l5.2-5.2
c0.5-0.5,1.4-0.5,1.9,0c0.5,0.5,0.5,1.4,0,1.9l-5.2,5.2L143.1,51.2z"/>
</g>
</g>
<g id="arrow-down">
<g>
<g>
<path d="M173.5,13.2l-6.8,7.4c-0.3,0.3-0.6,0.4-1,0.4c-0.4,0-0.7-0.2-1-0.4l-6.4-7.1c-0.5-0.5-0.4-1.3,0.1-1.8
c0.5-0.5,1.3-0.4,1.8,0.1l5.5,6l5.8-6.4c0.2-0.3,0.6-0.4,0.9-0.4c0.4,0,0.7,0.1,0.9,0.3C174,11.8,174,12.6,173.5,13.2z"/>
</g>
</g>
</g>
<g id="arrow-left">
<g>
<g>
<path d="M170.8,52.2c0,0.3-0.1,0.7-0.3,0.9c-0.2,0.3-0.5,0.4-0.9,0.4h-0.1c-0.3,0-0.6-0.1-0.8-0.3l-7-6.4
c-0.3-0.2-0.4-0.6-0.4-0.9c0-0.3,0.2-0.7,0.4-0.9l6.7-6.1c0.3-0.2,0.6-0.3,0.9-0.3c0.3,0,0.6,0.2,0.9,0.4
c0.5,0.5,0.4,1.3-0.1,1.8l-5.7,5.2l6,5.5C170.6,51.6,170.7,51.9,170.8,52.2z"/>
</g>
</g>
</g>
<g id="arrow-right">
<g>
<g>
<path d="M191.2,39.8c0-0.3,0.1-0.7,0.3-0.9c0.2-0.3,0.5-0.4,0.9-0.4h0.1c0.3,0,0.6,0.1,0.8,0.3l7,6.4c0.3,0.2,0.4,0.6,0.4,0.9
c0,0.3-0.2,0.7-0.4,0.9l-6.7,6.1c-0.3,0.2-0.6,0.3-0.9,0.3c-0.3,0-0.6-0.2-0.9-0.4c-0.5-0.5-0.4-1.3,0.1-1.8l5.7-5.2l-6-5.5
C191.4,40.4,191.3,40.1,191.2,39.8z"/>
</g>
</g>
</g>
<g id="arrow-up">
<g>
<g>
<path d="M189.8,20.8c-0.3,0-0.7-0.1-0.9-0.3c-0.3-0.2-0.4-0.5-0.4-0.9v-0.1c0-0.3,0.1-0.6,0.3-0.8l6.4-7c0.2-0.3,0.6-0.4,0.9-0.4
c0.3,0,0.7,0.2,0.9,0.4l6.1,6.7c0.2,0.3,0.3,0.6,0.3,0.9c0,0.3-0.2,0.6-0.4,0.9c-0.5,0.5-1.3,0.4-1.8-0.1l-5.2-5.7l-5.5,6
C190.5,20.6,190.1,20.8,189.8,20.8z"/>
</g>
</g>
</g>
<view id="icon-external" viewBox="30 60 30 30" />
<view id="icon-heart-view" viewBox="0 32 32 32" />
<view id="icon-arrow-right-view" viewBox="0 64 32 32" />
</svg>

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 658 KiB

View File

@ -4,7 +4,7 @@
<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>Ethereum Wallet | TREZOR</title>
<meta name="title" content="TrezorConnect" /> <meta name="title" content="TrezorConnect" />
<meta name="description" content="TrezorConnect" /> <meta name="description" content="TrezorConnect" />
<meta name="keywords" content="TrezorConnect" /> <meta name="keywords" content="TrezorConnect" />

View File

@ -35,7 +35,6 @@ export function loadTokensFromJSON(): any {
const ethERC20 = await httpRequest('data/ethERC20.json', 'json'); const ethERC20 = await httpRequest('data/ethERC20.json', 'json');
const devices: ?string = get('devices'); const devices: ?string = get('devices');
console.log("GET23", JSON.parse(devices))
if (devices) { if (devices) {
dispatch({ dispatch({
type: CONNECT.DEVICE_FROM_STORAGE, type: CONNECT.DEVICE_FROM_STORAGE,
@ -59,6 +58,14 @@ export function loadTokensFromJSON(): any {
}) })
} }
const pending: ?string = get('pending');
if (pending) {
dispatch({
type: 'PENDING.FROM_STORAGE',
payload: JSON.parse(pending)
})
}
const discovery: ?string = get('discovery'); const discovery: ?string = get('discovery');
if (discovery) { if (discovery) {
dispatch({ dispatch({
@ -88,12 +95,11 @@ export function loadTokensFromJSON(): any {
export const save = (key: string, value: string): any => { export const save = (key: string, value: string): any => {
return (dispatch, getState) => { return (dispatch, getState) => {
if (typeof window.localStorage !== 'undefined') { if (typeof window.localStorage !== 'undefined') {
//console.log("SAVEE!!!!", key, value)
try { try {
window.localStorage.setItem(key, value); window.localStorage.setItem(key, value);
} catch (error) { } catch (error) {
// available = false; // available = false;
console.error("ERROR: " + error) console.error("Local Storage ERROR: " + error)
} }
} }
} }
@ -102,7 +108,6 @@ export const save = (key: string, value: string): any => {
export const get = (key: string): ?string => { export const get = (key: string): ?string => {
if (typeof window.localStorage !== 'undefined') { if (typeof window.localStorage !== 'undefined') {
try { try {
console.log("GETTT", JSON.parse(window.localStorage.getItem(key)))
return window.localStorage.getItem(key); return window.localStorage.getItem(key);
} catch (error) { } catch (error) {
// available = false; // available = false;

View File

@ -0,0 +1,15 @@
/* @flow */
'use strict';
export const toggle = (): any => {
return (dispatch, getState) => {
if (!getState().log.opened) {
window.scrollTo(0, 0);
}
dispatch({
type: getState().log.opened ? 'log__close' : 'log__open'
});
}
}

View File

@ -65,6 +65,7 @@ export const showAddress = (address_n: string): any => {
return async (dispatch, getState) => { return async (dispatch, getState) => {
const selected = findSelectedDevice(getState().connect); const selected = findSelectedDevice(getState().connect);
if (!selected) return;
if (selected && !selected.connected) { if (selected && !selected.connected) {
dispatch({ dispatch({

View File

@ -4,7 +4,7 @@
import * as SEND from './constants/SendForm'; import * as SEND from './constants/SendForm';
import * as NOTIFICATION from './constants/notification'; import * as NOTIFICATION from './constants/notification';
import { getNonce, estimateGas, getGasPrice, pushTx } from './Web3Actions'; import { estimateGas, getGasPrice, pushTx } from './Web3Actions';
import EthereumjsUtil from 'ethereumjs-util'; import EthereumjsUtil from 'ethereumjs-util';
import EthereumjsUnits from 'ethereumjs-units'; import EthereumjsUnits from 'ethereumjs-units';
@ -18,7 +18,8 @@ import { initialState } from '../reducers/SendFormReducer';
import type { State, FeeLevel } from '../reducers/SendFormReducer'; import type { State, FeeLevel } from '../reducers/SendFormReducer';
import { findSelectedDevice } from '../reducers/TrezorConnectReducer'; import { findSelectedDevice } from '../reducers/TrezorConnectReducer';
const numberRegExp = new RegExp('^([0-9]{0,10}\\.)?[0-9]{1,18}$'); //const numberRegExp = new RegExp('^([0-9]{0,10}\\.)?[0-9]{1,18}$');
const numberRegExp = new RegExp('^(0|0\\.([0-9]+)?|[1-9]+\\.?([0-9]+)?|\\.[0-9]+)$');
const calculateFee = (gasPrice: string, gasLimit: string): string => { const calculateFee = (gasPrice: string, gasLimit: string): string => {
return EthereumjsUnits.convert( new BigNumber(gasPrice).times(gasLimit), 'gwei', 'ether'); return EthereumjsUnits.convert( new BigNumber(gasPrice).times(gasLimit), 'gwei', 'ether');
@ -50,7 +51,6 @@ export const getFeeLevels = (coin: string, gasPrice: BigNumber | string, gasLimi
const quarter: BigNumber = gasPrice.dividedBy(4); const quarter: BigNumber = gasPrice.dividedBy(4);
const high: string = gasPrice.plus(quarter.times(2)).toString(); const high: string = gasPrice.plus(quarter.times(2)).toString();
const low: string = gasPrice.minus(quarter.times(2)).toString(); const low: string = gasPrice.minus(quarter.times(2)).toString();
coin = coin.toUpperCase();
return [ return [
{ {
@ -106,11 +106,11 @@ export const init = (): any => {
// TODO: check if there are some unfinished tx in localStorage // TODO: check if there are some unfinished tx in localStorage
const { config } = getState().localStorage; const { config } = getState().localStorage;
const coin = config.coins.find(c => c.symbol === urlParams.coin); const coin = config.coins.find(c => c.network === urlParams.coin);
const gasPrice: BigNumber = new BigNumber( EthereumjsUnits.convert(web3instance.gasPrice, 'wei', 'gwei') ) || new BigNumber(coin.defaultGasPrice); const gasPrice: BigNumber = new BigNumber( EthereumjsUnits.convert(web3instance.gasPrice, 'wei', 'gwei') ) || new BigNumber(coin.defaultGasPrice);
const gasLimit: string = coin.defaultGasLimit.toString(); const gasLimit: string = coin.defaultGasLimit.toString();
const feeLevels: Array<FeeLevel> = getFeeLevels(urlParams.coin, gasPrice, gasLimit); const feeLevels: Array<FeeLevel> = getFeeLevels(coin.symbol, gasPrice, gasLimit);
// TODO: get nonce // TODO: get nonce
@ -119,6 +119,7 @@ export const init = (): any => {
checksum: selected.checksum, checksum: selected.checksum,
accountIndex: parseInt(urlParams.address), accountIndex: parseInt(urlParams.address),
coin: urlParams.coin, coin: urlParams.coin,
coinSymbol: coin.symbol,
token: urlParams.coin, token: urlParams.coin,
location: location.pathname, location: location.pathname,
@ -204,15 +205,32 @@ export const validation = (): any => {
errors.amount = 'Amount is not a number'; errors.amount = 'Amount is not a number';
} else { } else {
const account = getState().accounts.find(a => a.checksum === state.checksum && a.index === state.accountIndex && a.coin === state.coin); const account = getState().accounts.find(a => a.checksum === state.checksum && a.index === state.accountIndex && a.coin === state.coin);
let decimalRegExp;
if (state.token !== state.coin) { if (state.token !== state.coin) {
const tokenBalance: string = getState().tokens.find(t => t.ethAddress === account.address && t.symbol === state.token).balance; const token: any = getState().tokens.find(t => t.ethAddress === account.address && t.symbol === state.token);
if (new BigNumber(state.total).greaterThan(account.balance)) {
errors.amount = `Not enough ${ state.coin.toUpperCase() } to cover transaction fee`; if (parseInt(token.decimals) > 0) {
} else if (new BigNumber(state.amount).greaterThan(tokenBalance)) { decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,' + token.decimals + '})?|[1-9]+\\.?([0-9]{0,' + token.decimals + '})?|\\.[0-9]{1,' + token.decimals + '})$');
} else {
// decimalRegExp = new RegExp('^(0|0\\.?|[1-9]+\\.?)$');
decimalRegExp = new RegExp('^[0-9]+$');
}
if (!state.amount.match(decimalRegExp)) {
errors.amount = `Maximum ${ token.decimals} decimals allowed`;
} else if (new BigNumber(state.total).greaterThan(account.balance)) {
errors.amount = `Not enough ${ state.coinSymbol.toUpperCase() } to cover transaction fee`;
} else if (new BigNumber(state.amount).greaterThan(token.balance)) {
errors.amount = 'Not enough funds'; errors.amount = 'Not enough funds';
} else if (new BigNumber(state.amount).lessThanOrEqualTo('0')) {
errors.amount = 'Amount is too low';
} }
} else { } else {
if (new BigNumber(state.total).greaterThan(account.balance)) { decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,18})?|[1-9]+\\.?([0-9]{0,18})?|\\.[0-9]{0,18})$');
if (!state.amount.match(decimalRegExp)) {
errors.amount = `Maximum 18 decimals allowed`;
} else if (new BigNumber(state.total).greaterThan(account.balance)) {
errors.amount = 'Not enough funds'; errors.amount = 'Not enough funds';
} }
} }
@ -245,7 +263,7 @@ export const validation = (): any => {
const gp: BigNumber = new BigNumber(state.gasPrice); const gp: BigNumber = new BigNumber(state.gasPrice);
if (gp.greaterThan(100)) { if (gp.greaterThan(100)) {
errors.gasPrice = 'Gas price is too high'; errors.gasPrice = 'Gas price is too high';
} else if (gp.lessThan(1)) { } else if (gp.lessThanOrEqualTo('0')) {
errors.gasPrice = 'Gas price is too low'; errors.gasPrice = 'Gas price is too low';
} }
} }
@ -334,7 +352,7 @@ export const onCurrencyChange = (currency: any): any => {
} }
const { config } = getState().localStorage; const { config } = getState().localStorage;
const coin = config.coins.find(c => c.symbol === currentState.coin); const coin = config.coins.find(c => c.network === currentState.coin);
let gasLimit: string = ''; let gasLimit: string = '';
let amount: string = currentState.amount; let amount: string = currentState.amount;
@ -355,7 +373,7 @@ export const onCurrencyChange = (currency: any): any => {
total = calculateTotal(amount, currentState.gasPrice, currentState.gasLimit); total = calculateTotal(amount, currentState.gasPrice, currentState.gasLimit);
} }
const feeLevels: Array<FeeLevel> = getFeeLevels(currentState.coin, currentState.gasPrice, gasLimit); const feeLevels: Array<FeeLevel> = getFeeLevels(currentState.coinSymbol, currentState.gasPrice, gasLimit);
const state: State = { const state: State = {
...currentState, ...currentState,
@ -433,7 +451,7 @@ export const onFeeLevelChange = (feeLevel: any): any => {
// TODO: update value for custom fee // TODO: update value for custom fee
state.advanced = true; state.advanced = true;
feeLevel.gasPrice = state.gasPrice; feeLevel.gasPrice = state.gasPrice;
feeLevel.label = `${ calculateFee(state.gasPrice, state.gasLimit) } ${ state.coin.toUpperCase() }`; feeLevel.label = `${ calculateFee(state.gasPrice, state.gasLimit) } ${ state.coinSymbol }`;
} else { } else {
const customLevel = state.feeLevels.find(f => f.value === 'Custom'); const customLevel = state.feeLevels.find(f => f.value === 'Custom');
customLevel.label = ''; customLevel.label = '';
@ -462,12 +480,14 @@ export const onFeeLevelChange = (feeLevel: any): any => {
export const updateFeeLevels = (): any => { export const updateFeeLevels = (): any => {
return (dispatch, getState): void => { return (dispatch, getState): void => {
const currentState = getState().sendForm; const currentState = getState().sendForm;
const feeLevels: Array<FeeLevel> = getFeeLevels(currentState.coin, currentState.recommendedGasPrice, currentState.gasLimit); const feeLevels: Array<FeeLevel> = getFeeLevels(currentState.coinSymbol, currentState.recommendedGasPrice, currentState.gasLimit);
const selectedFeeLevel: ?FeeLevel = feeLevels.find(f => f.value === currentState.selectedFeeLevel.value)
const state: State = { const state: State = {
...currentState, ...currentState,
feeLevels, feeLevels,
selectedFeeLevel: feeLevels.find(f => f.value === currentState.selectedFeeLevel.value), selectedFeeLevel,
gasPrice: currentState.recommendedGasPrice, //gasPrice: currentState.recommendedGasPrice, // TODO HERE!
gasPrice: selectedFeeLevel.gasPrice, // TODO HERE!
gasPriceNeedsUpdate: false, gasPriceNeedsUpdate: false,
}; };
@ -507,7 +527,7 @@ export const onGasPriceChange = (gasPrice: string): any => {
if (gasPrice.match(numberRegExp) && state.gasLimit.match(numberRegExp)) { if (gasPrice.match(numberRegExp) && state.gasLimit.match(numberRegExp)) {
const customLevel = currentState.feeLevels.find(f => f.value === 'Custom'); const customLevel = currentState.feeLevels.find(f => f.value === 'Custom');
customLevel.gasPrice = gasPrice; customLevel.gasPrice = gasPrice;
customLevel.label = `${ calculateFee(gasPrice, state.gasLimit) } ${ state.coin.toUpperCase() }`; customLevel.label = `${ calculateFee(gasPrice, state.gasLimit) } ${ state.coinSymbol }`;
state.selectedFeeLevel = customLevel; state.selectedFeeLevel = customLevel;
@ -547,7 +567,7 @@ export const onGasLimitChange = (gasLimit: string): any => {
if (gasLimit.match(numberRegExp) && state.gasPrice.match(numberRegExp)) { if (gasLimit.match(numberRegExp) && state.gasPrice.match(numberRegExp)) {
const customLevel = currentState.feeLevels.find(f => f.value === 'Custom'); const customLevel = currentState.feeLevels.find(f => f.value === 'Custom');
customLevel.label = `${ calculateFee(state.gasPrice, gasLimit) } ${ state.coin.toUpperCase() }`; customLevel.label = `${ calculateFee(state.gasPrice, gasLimit) } ${ state.coinSymbol }`;
state.selectedFeeLevel = customLevel; state.selectedFeeLevel = customLevel;
@ -640,9 +660,6 @@ export const onSend = (): any => {
v: '' v: ''
} }
//const nonce = await getNonce(web3, currentAddress.address);
//txData.nonce = web3.toHex(nonce);
// const gasOptions = { // const gasOptions = {
// to: txData.to, // to: txData.to,
@ -660,6 +677,7 @@ export const onSend = (): any => {
// console.log("---->GASSS", txData, gasLimit, gasPrice, EthereumjsUnits.convert(gasPrice, 'gwei', 'wei')); // console.log("---->GASSS", txData, gasLimit, gasPrice, EthereumjsUnits.convert(gasPrice, 'gwei', 'wei'));
const selected = findSelectedDevice(getState().connect); const selected = findSelectedDevice(getState().connect);
if (!selected) return;
let signedTransaction = await TrezorConnect.ethereumSignTransaction({ let signedTransaction = await TrezorConnect.ethereumSignTransaction({
device: { device: {
@ -697,8 +715,11 @@ export const onSend = (): any => {
txData.s = '0x' + signedTransaction.data.s; txData.s = '0x' + signedTransaction.data.s;
txData.v = web3.toHex(signedTransaction.data.v); txData.v = web3.toHex(signedTransaction.data.v);
const gasLimit2 = await estimateGas(web3, txData); // const gasLimit2 = await estimateGas(web3, txData);
console.log("---->GASSS", txData, gasLimit2.toString() ); // console.log("---->GASSS", txData, gasLimit2.toString() );
const { config } = getState().localStorage;
const selectedCoin = config.coins.find(c => c.network === state.coin);
try { try {
const tx = new EthereumjsTx(txData); const tx = new EthereumjsTx(txData);
@ -708,6 +729,8 @@ export const onSend = (): any => {
dispatch({ dispatch({
type: SEND.TX_COMPLETE, type: SEND.TX_COMPLETE,
address: account, address: account,
token: state.token,
amount: state.amount,
txid, txid,
txData, txData,
}); });
@ -717,7 +740,7 @@ export const onSend = (): any => {
payload: { payload: {
type: 'success', type: 'success',
title: 'Transaction success', title: 'Transaction success',
message: `<a href="https://ropsten.etherscan.io/tx/${txid}">detail</a>`, message: `<a href="${ selectedCoin.explorer }/tx/${txid}" class="green" target="_blank" rel="noreferrer noopener">See transaction detail</a>`,
cancelable: true, cancelable: true,
actions: [] actions: []
} }
@ -736,23 +759,5 @@ export const onSend = (): any => {
} }
}); });
} }
// const tx = new EthereumjsTx(txData);
// console.log("2222", tx, tx.toJSON(), tx.from, tx.to);
// const serializedTx = '0x' + tx.serialize().toString('hex');
// console.log("----> PUSZ TX", web3, currentAddress, serializedTx)
// const txid = await pushTx(web3, serializedTx);
// console.log("----> PUSZ TX2", web3, serializedTx)
// dispatch({
// type: SEND.TX_COMPLETE,
// address: currentAddress,
// txid,
// txData,
// })
// const [ url ] = getState().router.location.pathname.split('/send');
// dispatch( push(url) );
} }
} }

View File

@ -106,11 +106,12 @@ export const onDetailsToggle = (): any => {
export const loadTokens = (input: string): any => { export const loadTokens = (input: string, account: any): any => {
return async (dispatch, getState): Promise<any> => { return async (dispatch, getState): Promise<any> => {
if (input.length < 1) return null; if (input.length < 1) return null;
// TODO (eth tokens, etc tokens, ropsten tokens ...)
const { ethTokens } = getState().localStorage; const { ethTokens } = getState().localStorage;
const value = input.toLowerCase(); const value = input.toLowerCase();
@ -121,18 +122,14 @@ export const loadTokens = (input: string): any => {
); );
//const result = ethTokens.filter(t => t.symbol.toLowerCase().indexOf(lower) >= 0); //const result = ethTokens.filter(t => t.symbol.toLowerCase().indexOf(lower) >= 0);
console.log("RESULT!", result.length, result)
if (result.length > 0) { if (result.length > 0) {
return { options: result }; return { options: result };
} else { } else {
const web3instance = getState().web3.find(w3 => w3.coin === 'eth'); const web3instance = getState().web3.find(w3 => w3.coin === account.coin);
const info = await getTokenInfoAsync(web3instance.erc20, input); const info = await getTokenInfoAsync(web3instance.erc20, input);
info.address = input; info.address = input;
console.log("FETCH", info)
if (info) { if (info) {
return { return {
options: [ info ] options: [ info ]
@ -143,13 +140,9 @@ export const loadTokens = (input: string): any => {
} }
} }
//await resolveAfter(300000); //await resolveAfter(300000);
//await resolveAfter(3000); //await resolveAfter(3000);
} }
} }
@ -158,10 +151,7 @@ export const loadTokens = (input: string): any => {
export const selectToken = (token: any, account: any): any => { export const selectToken = (token: any, account: any): any => {
return async (dispatch, getState): Promise<any> => { return async (dispatch, getState): Promise<any> => {
console.warn("ADD", token, account)
const web3instance = getState().web3.find(w3 => w3.coin === account.coin); const web3instance = getState().web3.find(w3 => w3.coin === account.coin);
dispatch({ dispatch({
type: TOKEN.ADD, type: TOKEN.ADD,
payload: { payload: {
@ -171,7 +161,6 @@ export const selectToken = (token: any, account: any): any => {
} }
}); });
// TODO: load token balance
const tokenBalance = await getTokenBalanceAsync(web3instance.erc20, token.address, account.address); const tokenBalance = await getTokenBalanceAsync(web3instance.erc20, token.address, account.address);
dispatch({ dispatch({
type: TOKEN.SET_BALANCE, type: TOKEN.SET_BALANCE,
@ -185,6 +174,13 @@ export const selectToken = (token: any, account: any): any => {
} }
} }
export const removeToken = (token: any): any => {
return {
type: TOKEN.REMOVE,
token
}
}
export const onTokenSearch = (search: string): any => { export const onTokenSearch = (search: string): any => {

View File

@ -19,7 +19,7 @@ import { getTransactionHistory } from '../services/EtherscanService';
import { push } from 'react-router-redux'; import { push } from 'react-router-redux';
import { init as initWeb3, getNonce, getBalanceAsync, getTokenBalanceAsync } from './Web3Actions'; import { init as initWeb3, getNonceAsync, getBalanceAsync, getTokenBalanceAsync } from './Web3Actions';
import type { Discovery } from '../reducers/DiscoveryReducer'; import type { Discovery } from '../reducers/DiscoveryReducer';
import { resolveAfter } from '../utils/promiseUtils'; import { resolveAfter } from '../utils/promiseUtils';
@ -53,6 +53,10 @@ export const init = (): any => {
try { try {
await TrezorConnect.init({ await TrezorConnect.init({
transport_reconnect: true, transport_reconnect: true,
coins_src: './data/coins.json',
firmware_releases_src: './data/releases-1.json',
transport_config_src: './data/config_signed.bin',
latest_bridge_src: './data/latest.txt'
}); });
setTimeout(() => { setTimeout(() => {
@ -116,12 +120,19 @@ export const postInit = (): any => {
export const initConnectedDevice = (device: any): any => { export const initConnectedDevice = (device: any): any => {
return (dispatch, getState): void => { return (dispatch, getState): void => {
//dispatch( onSelectDevice(device) );
const selected = findSelectedDevice(getState().connect); const selected = findSelectedDevice(getState().connect);
if (device.unacquired && selected && selected.path !== device.path && !selected.connected) { if (selected && selected.checksum) {
dispatch( onSelectDevice(device) ); dispatch( onSelectDevice(device) );
} else if (!selected) { } else if (!selected) {
dispatch( onSelectDevice(device) ); dispatch( onSelectDevice(device) );
} }
// if (device.unacquired && selected && selected.path !== device.path && !selected.connected) {
// dispatch( onSelectDevice(device) );
// } else if (!selected) {
// dispatch( onSelectDevice(device) );
// }
} }
} }
@ -235,47 +246,27 @@ export const getSelectedDeviceState = (): any => {
export const deviceDisconnect = (device: any): any => { export const deviceDisconnect = (device: any): any => {
return async (dispatch, getState): Promise<void> => { return async (dispatch, getState): Promise<void> => {
if (!device || !device.features) return null;
const selected = findSelectedDevice(getState().connect); const selected = findSelectedDevice(getState().connect);
if (selected && selected.features.device_id === device.features.device_id) {
stopDiscoveryProcess(selected); if (device && device.features) {
if (selected && selected.features.device_id === device.features.device_id) {
stopDiscoveryProcess(selected);
}
const affected = getState().connect.devices.filter(d => d.features && d.checksum && !d.remember && d.features.device_id === device.features.device_id);
if (affected.length > 0) {
dispatch({
type: CONNECT.REMEMBER_REQUEST,
device,
allInstances: affected
});
}
} }
const affected = getState().connect.devices.filter(d => d.features && d.checksum && !d.remember && d.features.device_id === device.features.device_id);
if (affected.length > 0) {
dispatch({
type: CONNECT.REMEMBER_REQUEST,
device,
allInstances: affected
});
}
// if (selected && selected.checksum) {
// if (device.features && device.features.device_id === selected.features.device_id) {
// stopDiscoveryProcess(selected);
// }
// }
// // stop running discovery process on this device
// if (selected && selected.path === device.path){
// if (selected.checksum) {
// stopDiscoveryProcess(selected);
// }
// }
// // check if disconnected device was remembered before.
// // request modal if not
// const affected = getState().connect.devices.filter(d => d.path === device.path && d.checksum && !device.remember);
// check if reload is needed
if (!selected) { if (!selected) {
dispatch( switchToFirstAvailableDevice() ); dispatch( switchToFirstAvailableDevice() );
} }
} }
} }
@ -397,7 +388,7 @@ export const beginDiscoveryProcess = (device: any, coin: string): any => {
return async (dispatch, getState) => { return async (dispatch, getState) => {
const { config } = getState().localStorage; const { config } = getState().localStorage;
const coinToDiscover = config.coins.find(c => c.symbol === coin); const coinToDiscover = config.coins.find(c => c.network === coin);
// TODO: validate device checksum // TODO: validate device checksum
// const checksum = await __acquire(device.path, device.instance); // const checksum = await __acquire(device.path, device.instance);
@ -455,7 +446,7 @@ export const beginDiscoveryProcess = (device: any, coin: string): any => {
// send data to reducer // send data to reducer
dispatch({ dispatch({
type: DISCOVERY.START, type: DISCOVERY.START,
coin: coinToDiscover.shortcut, coin: coinToDiscover.network,
device, device,
xpub: response.data.publicKey, xpub: response.data.publicKey,
basePath, basePath,
@ -574,7 +565,7 @@ export const discoverAddress = (device: any, discoveryProcess: Discovery): any =
}) })
} }
const nonce = await getNonce(web3instance.web3, ethAddress); const nonce = await getNonceAsync(web3instance.web3, ethAddress);
if (discoveryProcess.interrupted) return; if (discoveryProcess.interrupted) return;
dispatch({ dispatch({
type: ADDRESS.SET_NONCE, type: ADDRESS.SET_NONCE,

View File

@ -15,6 +15,41 @@ import { httpRequest } from '../utils/networkUtils';
type ActionMethod = (dispatch: any, getState: any) => Promise<any>; type ActionMethod = (dispatch: any, getState: any) => Promise<any>;
type Web3Payload =
| {
name: string;
instance: Web3;
chainId: number;
erc20abi: any;
}
| {
network: string;
blockHash: string;
}
| {
network: string;
gasPrice: string;
}
| {
network: string;
address: string;
balance: string;
}
| {
network: string;
address: string;
nonce: string;
}
| {
network: string;
blockHash: string;
};
type Web3Action = {
type: string,
payload?: Web3Payload
};
export function init(web3: ?Web3, coinIndex: number = 0): ActionMethod { export function init(web3: ?Web3, coinIndex: number = 0): ActionMethod {
return async (dispatch, getState) => { return async (dispatch, getState) => {
@ -30,7 +65,7 @@ export function init(web3: ?Web3, coinIndex: number = 0): ActionMethod {
return; return;
} }
const coinName = coin.shortcut; const coinName = coin.network;
const urls = coin.backends[0].urls; const urls = coin.backends[0].urls;
let web3host: string = urls[0]; let web3host: string = urls[0];
@ -106,36 +141,42 @@ export function init(web3: ?Web3, coinIndex: number = 0): ActionMethod {
//const shh = instance.shh.newIdentity(); //const shh = instance.shh.newIdentity();
const latestBlockFilter = instance.eth.filter('latest'); const latestBlockFilter = instance.eth.filter('latest');
latestBlockFilter.watch(async (error, blockHash) => {
const onBlockMined = async (error, blockHash) => {
if (error) { if (error) {
console.warn("ERROR!", error);
// setInterval(() => { window.setTimeout(() => {
// dispatch( getGasPrice(coinName) ); // try again
// }, 5000); onBlockMined("manually_triggered_error", undefined);
}, 30000);
}
if (blockHash) {
dispatch({
type: WEB3.BLOCK_UPDATED,
name: coinName,
blockHash
});
} }
dispatch({
type: WEB3.BLOCK_UPDATED,
name: coinName,
blockHash
});
// TODO: filter only current device // TODO: filter only current device
const accounts = getState().accounts.filter(a => a.coin === coinName); const accounts = getState().accounts.filter(a => a.coin === coinName);
for (const addr of accounts) { for (const addr of accounts) {
dispatch( getBalance(addr) ); dispatch( getBalance(addr) );
dispatch( getNonce(addr) );
} }
dispatch( getGasPrice(coinName) ); dispatch( getGasPrice(coinName) );
// if (pendingTxs.length > 0) { const pending = getState().pending.filter(p => p.coin === coinName);
// for (const tx of pendingTxs) { for (const tx of pending) {
// dispatch( getTransactionReceipt(tx) ); dispatch( getTransactionReceipt(tx) );
// } }
// }
}); }
latestBlockFilter.watch(onBlockMined);
// init next coin // init next coin
dispatch( init(instance, coinIndex + 1) ); dispatch( init(instance, coinIndex + 1) );
@ -238,18 +279,46 @@ export function getBalance(addr: Address): ActionMethod {
} }
} }
export function getTransactionReceipt(txid: string): any { export function getNonce(addr: Address) {
return async (dispatch, getState) => { return async (dispatch, getState) => {
const { web3 } = getState().web3;
//web3.eth.getTransactionReceipt(txid, (error, tx) => { const web3instance = getState().web3.filter(w3 => w3.coin === addr.coin)[0];
web3.eth.getTransaction(txid, (error, tx) => { const web3 = web3instance.web3;
if (tx && tx.blockNumber) {
web3.eth.getBlock(tx.blockHash, (error, block) => { web3.eth.getTransactionCount(addr.address, (error, result) => {
console.log("---MAMM BLOCK", error, block, tx, tx.blockHash) if (!error) {
if (addr.nonce !== result) {
dispatch({ dispatch({
type: ACTIONS.TX_CONFIRMED, type: ADDRESS.SET_NONCE,
txid, address: addr.address,
nonce: result
});
}
}
});
}
}
export function getTransactionReceipt(tx: any): any {
return async (dispatch, getState) => {
const web3instance = getState().web3.filter(w3 => w3.coin === tx.coin)[0];
const web3 = web3instance.web3;
//web3.eth.getTransactionReceipt(txid, (error, tx) => {
web3.eth.getTransaction(tx.id, (error, receipt) => {
console.log("RECEIP", receipt)
if (receipt && receipt.blockNumber) {
web3.eth.getBlock(receipt.blockHash, (error, block) => {
console.log("---MAMM BLOCK", error, block, receipt, receipt.blockHash)
dispatch({
//type: ACTIONS.TX_CONFIRMED,
type: WEB3.PENDING_TX_RESOLVED,
tx, tx,
receipt,
block block
}) })
}); });
@ -306,7 +375,7 @@ export const getTokenBalanceAsync = (erc20: any, token: any, address: any): Prom
}); });
} }
export function getNonce(web3, address) { export function getNonceAsync(web3, address) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
web3.eth.getTransactionCount(address, (error, result) => { web3.eth.getTransactionCount(address, (error, result) => {
if (error) { if (error) {
@ -389,81 +458,6 @@ export function pushTx(web3, tx) {
}) })
} }
export function composeTransaction() {
return async function (dispatch, getState) {
const { web3 } = getState().web3;
const { address, amount } = getState().sendForm;
const resp = await TrezorConnect.getPublicKey({ path: "m/44'/60'/0'/0", confirmation: false });
const hdk = new HDKey();
hdk.publicKey = new Buffer(resp.data.publicKey, 'hex');
hdk.chainCode = new Buffer(resp.data.chainCode, 'hex');
const derivedKey = hdk.derive("m/0");
const myAddress = EthereumjsUtil.publicToAddress(derivedKey.publicKey, true);
const txData = {
address_n: [
(44 | 0x80000000) >>> 0,
(60 | 0x80000000) >>> 0,
(0 | 0x80000000) >>> 0,
0, 0
],
to: address,
value: web3.toHex(web3.toWei(amount, 'ether')),
data,
chainId: 3
}
console.log("NONCE", myAddress)
const nonce = await getNonce(web3, '0x' + myAddress.toString('hex') );
console.log("NONCE", nonce)
const gasOptions = {
to: txData.to,
data: txData.data
}
const gasLimit = await estimateGas(web3, gasOptions);
const gasPrice = await getGasPrice(web3);
txData.nonce = web3.toHex(nonce);
txData.gasLimit = web3.toHex(gasLimit);
txData.gasPrice = web3.toHex(gasPrice);
console.log("NONCE", nonce, gasLimit, gasPrice)
let signedTransaction = await TrezorConnect.ethereumSignTransaction({
//path: "m/44'/60'/0'/0/0",
address_n: txData.address_n,
nonce: strip(txData.nonce),
gas_price: strip(txData.gasPrice),
gas_limit: strip(txData.gasLimit),
to: strip(txData.to),
value: strip(txData.value),
data: txData.data,
chain_id: txData.chainId
});
txData.r = '0x' + signedTransaction.data.r;
txData.s = '0x' + signedTransaction.data.s;
txData.v = web3.toHex(signedTransaction.data.v);
const tx = new EthereumjsTx(txData);
const serializedTx = '0x' + tx.serialize().toString('hex');
const txid = await pushTx(web3, serializedTx);
dispatch({
type: 'tx_complete',
txid
})
console.log("TXID", txid);
}
}

View File

@ -6,4 +6,5 @@ export const STOP: string = 'web3__stop';
export const CREATE: string = 'web3__create'; export const CREATE: string = 'web3__create';
export const READY: string = 'web3__ready'; export const READY: string = 'web3__ready';
export const BLOCK_UPDATED: string = 'web3__block_updated'; export const BLOCK_UPDATED: string = 'web3__block_updated';
export const GAS_PRICE_UPDATED: string = 'web3__gas_price_updated'; export const GAS_PRICE_UPDATED: string = 'web3__gas_price_updated';
export const PENDING_TX_RESOLVED: string = 'web3__pending_tx_resolved';

View File

@ -15,7 +15,6 @@ export const ON_GAS_PRICE_CHANGE: string = 'send__on_gas_price_change';
export const ON_GAS_LIMIT_CHANGE: string = 'send__on_gas_limit_change'; export const ON_GAS_LIMIT_CHANGE: string = 'send__on_gas_limit_change';
export const ON_TX_DATA_CHANGE: string = 'send__on_data_change'; export const ON_TX_DATA_CHANGE: string = 'send__on_data_change';
export const ON_TX_SEND: string = 'send__on_send'; export const ON_TX_SEND: string = 'send__on_send';
export const ON_TX_COMPLETE: string = 'send__on_tx_complete';
export const ON_GAS_PRICE_UPDATE: string = 'send__on_gas_price_update'; export const ON_GAS_PRICE_UPDATE: string = 'send__on_gas_price_update';

View File

@ -2,16 +2,31 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as LogActions from '../../actions/LogActions';
const Footer = (props: any): any => { const Footer = (props: any): any => {
return ( return (
<footer> <footer>
<span>© 2018</span> <span>© 2018</span>
<a href="http://satoshilabs.com" target="_blank" className="satoshi green">SatoshiLabs</a> <a href="http://satoshilabs.com" target="_blank" rel="noreferrer noopener" className="satoshi green">SatoshiLabs</a>
<a href="tos.pdf" target="_blank" className="green">Terms</a> <a href="tos.pdf" target="_blank" rel="noreferrer noopener" className="green">Terms</a>
<a onClick={ props.showLog } className="green">Show Log</a> <a onClick={ props.toggle } className="green">Show Log</a>
</footer> </footer>
); );
} }
export default Footer; export default connect(
(state) => {
return {
}
},
(dispatch) => {
return {
toggle: bindActionCreators(LogActions.toggle, dispatch),
};
}
)(Footer);

View File

@ -8,9 +8,26 @@ export default class Header extends Component {
return ( return (
<header> <header>
<div className="layout-wrapper"> <div className="layout-wrapper">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2567.5 722.3" width="100%" height="100%" preserveAspectRatio="xMinYMin meet"><path d="M1186 2932.6h46.2v147H1186v-147z"></path><path d="M249 0C149.9 0 69.7 80.2 69.7 179.3v67.2C34.9 252.8 0 261.2 0 272.1v350.7s0 9.7 10.9 14.3c39.5 16 194.9 71 230.6 83.6 4.6 1.7 5.9 1.7 7.1 1.7 1.7 0 2.5 0 7.1-1.7 35.7-12.6 191.5-67.6 231-83.6 10.1-4.2 10.5-13.9 10.5-13.9V272.1c0-10.9-34.4-19.7-69.3-25.6v-67.2C428.4 80.2 347.7 0 249 0zm0 85.7c58.4 0 93.7 35.3 93.7 93.7v58.4c-65.5-4.6-121.4-4.6-187.3 0v-58.4c0-58.5 35.3-93.7 93.6-93.7zm-.4 238.1c81.5 0 149.9 6.3 149.9 17.6v218.8c0 3.4-.4 3.8-3.4 5-2.9 1.3-139 50.4-139 50.4s-5.5 1.7-7.1 1.7c-1.7 0-7.1-2.1-7.1-2.1s-136.1-49.1-139-50.4-3.4-1.7-3.4-5V341c-.8-11.3 67.6-17.2 149.1-17.2z"></path><g transform="translate(91.363 -287.434) scale(.95575)"><path d="M666.6 890V639.3H575v-89.9h285.6v89.9h-90.7V890H666.6z"></path><path d="M1092 890l-47-107.1h-37.4V890H904.3V549.4h181.8c79.8 0 122.6 52.9 122.6 116.7 0 58.8-34 89.9-61.3 103.3l61.7 120.5H1092zm12.2-223.9c0-18.5-16.4-26.5-33.6-26.5h-63v53.8h63c17.2-.4 33.6-8.4 33.6-27.3z"></path><path d="M1262.9 890V549.4h258.3v89.9h-155.4v33.6h151.6v89.9h-151.6v37.4h155.4V890h-258.3z"></path><path d="M1574.9 890.4v-81.9l129.8-168.8h-129.8v-89.9h265.8v81.1l-130.2 169.7h134v89.9l-269.6-.1z"></path><path d="M1869.7 720.3c0-104.6 81.1-176.4 186.5-176.4 105 0 186.5 71.4 186.5 176.4 0 104.6-81.1 176-186.5 176s-186.5-71.4-186.5-176zm268 0c0-47.5-32.3-85.3-81.9-85.3-49.6 0-81.9 37.8-81.9 85.3s32.3 85.3 81.9 85.3c50 0 81.9-37.8 81.9-85.3z"></path><path d="M2473.6 890.4l-47-107.1h-37.4v107.1h-103.3V549.8h181.8c79.8 0 122.6 52.9 122.6 116.7 0 58.8-34 89.9-61.3 103.3l61.7 120.5h-117.1zm12.6-224.3c0-18.5-16.4-26.5-33.6-26.5h-63v53.8h63c17.3-.4 33.6-8.4 33.6-27.3z"></path></g></svg> <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 163.7 41.9" width="100%" height="100%" preserveAspectRatio="xMinYMin meet">
<polygon points="101.1,12.8 118.2,12.8 118.2,17.3 108.9,29.9 118.2,29.9 118.2,35.2 101.1,35.2 101.1,30.7 110.4,18.1 101.1,18.1"/>
<path d="M158.8,26.9c2.1-0.8,4.3-2.9,4.3-6.6c0-4.5-3.1-7.4-7.7-7.4h-10.5v22.3h5.8v-7.5h2.2l4.1,7.5h6.7L158.8,26.9z M154.7,22.5 h-4V18h4c1.5,0,2.5,0.9,2.5,2.2C157.2,21.6,156.2,22.5,154.7,22.5z"/>
<path d="M130.8,12.5c-6.8,0-11.6,4.9-11.6,11.5s4.9,11.5,11.6,11.5s11.7-4.9,11.7-11.5S137.6,12.5,130.8,12.5z M130.8,30.3 c-3.4,0-5.7-2.6-5.7-6.3c0-3.8,2.3-6.3,5.7-6.3c3.4,0,5.8,2.6,5.8,6.3C136.6,27.7,134.2,30.3,130.8,30.3z"/>
<polygon points="82.1,12.8 98.3,12.8 98.3,18 87.9,18 87.9,21.3 98,21.3 98,26.4 87.9,26.4 87.9,30 98.3,30 98.3,35.2 82.1,35.2 "/>
<path d="M24.6,9.7C24.6,4.4,20,0,14.4,0S4.2,4.4,4.2,9.7v3.1H0v22.3h0l14.4,6.7l14.4-6.7h0V12.9h-4.2V9.7z M9.4,9.7 c0-2.5,2.2-4.5,5-4.5s5,2,5,4.5v3.1H9.4V9.7z M23,31.5l-8.6,4l-8.6-4V18.1H23V31.5z"/>
<path d="M79.4,20.3c0-4.5-3.1-7.4-7.7-7.4H61.2v22.3H67v-7.5h2.2l4.1,7.5H80l-4.9-8.3C77.2,26.1,79.4,24,79.4,20.3z M71,22.5h-4V18 h4c1.5,0,2.5,0.9,2.5,2.2C73.5,21.6,72.5,22.5,71,22.5z"/>
<polygon points="40.5,12.8 58.6,12.8 58.6,18.1 52.4,18.1 52.4,35.2 46.6,35.2 46.6,18.1 40.5,18.1 "/>
</svg>
<div>
<a href="https://trezor.io/" target="_blank" rel="noreferrer noopener">TREZOR</a>
<a href="https://doc.satoshilabs.com/trezor-user/" target="_blank" rel="noreferrer noopener">Docs</a>
<a href="https://blog.trezor.io/" target="_blank" rel="noreferrer noopener">Blog</a>
<a href="https://trezor.io/support/" target="_blank" rel="noreferrer noopener">Support</a>
</div>
</div> </div>
</header> </header>
); );
} }
} }

View File

@ -5,36 +5,32 @@ import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import * as SendFormActions from '../../actions/SendFormActions'; import * as LogActions from '../../actions/LogActions';
import { getAddress } from '../../actions/TrezorConnectActions';
const Log = (props: any) => { const Log = (props: any) => {
if (!props.log.opened)
return null;
return ( return (
<details className="log"> <div className="log">
Log <button className="log-close transparent" onClick={ props.toggle }></button>
</details> <h2>Log</h2>
<p>Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history.</p>
<textarea></textarea>
</div>
) )
} }
function mapStateToProps(state, own) {
}
function mapDispatchToProps(dispatch) {
}
export default connect( export default connect(
(state) => { (state) => {
return { return {
accounts: state.accounts, log: state.log
receive: state.receive
}; };
}, },
(dispatch) => { (dispatch) => {
return { return {
getAddress: bindActionCreators(getAddress, dispatch), toggle: bindActionCreators(LogActions.toggle, dispatch),
}; };
} }
)(Log); )(Log);

View File

@ -25,10 +25,10 @@ export default (props: any): any => {
</svg> </svg>
<span>Connect TREZOR to continue</span> <span>Connect TREZOR to continue</span>
</p> </p>
{/* <p>Don't have TREZOR? <a href="https://trezor.io/" target="_blank">Get one</a></p> */} {/* <p>Don't have TREZOR? <a href="https://trezor.io/" target="_blank" rel="noreferrer noopener">Get one</a></p> */}
</div> </div>
<div className="image"></div> <div className="image"></div>
<p>Don't have TREZOR? <a href="https://trezor.io/" className="green" target="_blank">Get one</a></p> <p>Don't have TREZOR? <a href="https://trezor.io/" className="green" target="_blank" rel="noreferrer noopener">Get one</a></p>
</main> </main>
<Footer /> <Footer />
</div> </div>

View File

@ -8,6 +8,7 @@ import LocalStorageError from './LocalStorageError';
import TrezorConnectError from './TrezorConnectError'; import TrezorConnectError from './TrezorConnectError';
import Header from '../common/Header'; import Header from '../common/Header';
import Footer from '../common/Footer'; import Footer from '../common/Footer';
import Log from '../common/Log';
import { Notification } from '../common/Notification'; import { Notification } from '../common/Notification';
export default (props: any): any => { export default (props: any): any => {
@ -40,6 +41,7 @@ export default (props: any): any => {
<div className="app connect-device"> <div className="app connect-device">
<Header /> <Header />
{ notification } { notification }
<Log />
<main> <main>
<h2 className="claim">The private bank in your hands.</h2> <h2 className="claim">The private bank in your hands.</h2>
<p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p> <p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p>
@ -59,10 +61,11 @@ export default (props: any): any => {
</span> </span>
</p> </p>
{/* <button>Add new device</button> */} {/* <button>Add new device</button> */}
{/* <p>Don't have TREZOR? <a href="https://trezor.io/" target="_blank">Get one</a></p> */} {/* <p>Don't have TREZOR? <a href="https://trezor.io/" target="_blank" rel="noreferrer noopener">Get one</a></p> */}
</div>
<div className="image">
<p>Don't have TREZOR? <a href="https://trezor.io/" className="green" target="_blank" rel="noreferrer noopener">Get one</a></p>
</div> </div>
<div className="image"></div>
<p>Don't have TREZOR? <a href="https://trezor.io/" className="green" target="_blank">Get one</a></p>
</main> </main>
<Footer /> <Footer />
</div> </div>

View File

@ -7,6 +7,8 @@ import { findSelectedDevice } from '../../reducers/TrezorConnectReducer';
const ConfirmAddress = (props: any): any => { const ConfirmAddress = (props: any): any => {
const account = props.accounts.find(a => a.checksum === props.receive.checksum && a.index === props.receive.accountIndex && a.coin === props.receive.coin); const account = props.accounts.find(a => a.checksum === props.receive.checksum && a.index === props.receive.accountIndex && a.coin === props.receive.coin);
const { config } = props.localStorage;
const selectedCoin = config.coins.find(c => c.network === account.coin);
return ( return (
<div className="confirm-address"> <div className="confirm-address">
@ -16,7 +18,7 @@ const ConfirmAddress = (props: any): any => {
</div> </div>
<div className="content"> <div className="content">
<p>{ account.address }</p> <p>{ account.address }</p>
<label>{ account.coin.toUpperCase() } account #{ (account.index + 1) }</label> <label>{ selectedCoin.symbol } account #{ (account.index + 1) }</label>
</div> </div>
</div> </div>
); );

View File

@ -8,6 +8,7 @@ const Confirmation = (props): any => {
amount, amount,
address, address,
coin, coin,
coinSymbol,
token, token,
total, total,
selectedFeeLevel selectedFeeLevel
@ -21,7 +22,7 @@ const Confirmation = (props): any => {
</div> </div>
<div className="content"> <div className="content">
<label>Send </label> <label>Send </label>
<p>{ `${amount} ${token.toUpperCase() }` }</p> <p>{ `${amount} ${ coinSymbol }` }</p>
<label>To</label> <label>To</label>
<p>{ address }</p> <p>{ address }</p>
<label>Fee</label> <label>Fee</label>

View File

@ -103,6 +103,7 @@ const mapStateToProps = (state: any, own: any): any => {
devices: state.connect.devices, devices: state.connect.devices,
sendForm: state.sendForm, sendForm: state.sendForm,
receive: state.receive, receive: state.receive,
localStorage: state.localStorage
}; };
} }

View File

@ -24,7 +24,7 @@ export default class PinModal extends Component {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
console.warn("PROPZ", props) // check if this device is already known
const isSavedDevice = props.devices.find(d => d.path === props.modal.device.path && d.remember); const isSavedDevice = props.devices.find(d => d.path === props.modal.device.path && d.remember);
this.state = { this.state = {

View File

@ -133,8 +133,8 @@ export default class Pin extends Component {
<button type="button" data-value="3" onClick={ event => this.onPinAdd(3) }>&#8226;</button> <button type="button" data-value="3" onClick={ event => this.onPinAdd(3) }>&#8226;</button>
</div> </div>
<div><button className="submit" type="button" onClick={ event => onPinSubmit(pin) }>Enter pin</button></div> <div><button className="submit" type="button" onClick={ event => onPinSubmit(pin) }>Enter PIN</button></div>
<p>Not sure how PIN works? <a className="green" href="http://doc.satoshilabs.com/trezor-user/enteringyourpin.html" target="_blank">Learn more</a></p> <p>Not sure how PIN works? <a className="green" href="http://doc.satoshilabs.com/trezor-user/enteringyourpin.html" target="_blank" rel="noreferrer noopener">Learn more</a></p>
</div> </div>
); );
} }

View File

@ -2,17 +2,36 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { Notification } from '../common/Notification';
const Acquire = (props: any): any => { const Acquire = (props: any): any => {
const actions = [
{
label: 'Acquire device',
callback: () => {
props.acquireDevice()
}
}
];
return ( return (
<section className="acquire"> <section className="acquire">
<div className="warning"> <Notification
title="Device is used in other window"
message="Do you want to use your device in this window?"
className="info"
cancelable={false}
actions={actions}
close={ () => {} }
/>
{/* <div className="warning">
<div> <div>
<h2>Device is used in other window</h2> <h2></h2>
<p>Do you want to use your device in this window?</p> <p></p>
</div> </div>
<button onClick={ event => props.acquireDevice() }>Acquire device</button> <button onClick={ event => props.acquireDevice() }>Acquire device</button>
</div> </div> */}
</section> </section>
); );
} }

View File

@ -38,7 +38,7 @@ const History = (props): any => {
return ( return (
<div key={i} className="history-pending-transaction"> <div key={i} className="history-pending-transaction">
<a href={ etherscanLink } target="_blank">Details</a> <a href={ etherscanLink } target="_blank" rel="noreferrer noopener">Details</a>
<span className="address">{ tx.to }</span> <span className="address">{ tx.to }</span>
Pending... Pending...
</div> </div>

View File

@ -14,25 +14,6 @@ export default class AbstractAccount extends Component {
this.props.updateAccount(); this.props.updateAccount();
} }
// shouldInitAccount(newProps: any): boolean {
// const locationChanged: boolean = newProps.location.pathname !== this.props.location.pathname;
// const accountNotLoaded: boolean = !newProps.detail.loaded && !this.props.detail.loaded;
// return (locationChanged || accountNotLoaded);
// }
// shouldUpdateAccount(newProps: any): boolean {
// const { detail } = this.props;
// const loaded: boolean = detail.loaded;
// if (detail.address === '') {
// const currentAccount = this.props.accounts.find(a => a.index === detail.addressIndex && a.coin === detail.coin && a.checksum === detail.checksum);
// }
// // return (loaded && );
// }
componentWillUnmount() { componentWillUnmount() {
this.props.disposeAccount(); this.props.disposeAccount();
} }

View File

@ -1,9 +1,62 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
import React from 'react'; import React, { Component } from 'react';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
type State = {
style: any;
}
class Indicator extends Component {
state: State;
constructor(props: any) {
super(props);
this.state = {
style: {
width: 0,
left: 0
},
}
}
componentDidMount() {
this.reposition();
}
componentDidUpdate(newProps: any) {
this.reposition();
}
reposition() {
const tabs = document.querySelector('.account-tabs');
const active = tabs.querySelector('.active');
const bounds = active.getBoundingClientRect();
const left = bounds.left - tabs.getBoundingClientRect().left;
if (this.state.style.left !== left) {
this.setState({
style: {
width: bounds.width,
left: left,
}
})
}
}
render() {
return (
<div className="indicator" style={ this.state.style }>{ this.props.pathname }</div>
);
}
}
const AccountTabs = (props: any): any => { const AccountTabs = (props: any): any => {
const urlParams = props.match.params; const urlParams = props.match.params;
@ -24,9 +77,10 @@ const AccountTabs = (props: any): any => {
<NavLink to={ `${basePath}/receive` }> <NavLink to={ `${basePath}/receive` }>
Receive Receive
</NavLink> </NavLink>
<NavLink to={ `${basePath}/signverify` }> {/* <NavLink to={ `${basePath}/signverify` }>
Sign &amp; Verify Sign &amp; Verify
</NavLink> </NavLink> */}
<Indicator pathname={props.match.pathname } />
</div> </div>
); );
} }

View File

@ -8,6 +8,8 @@ import BigNumber from 'bignumber.js';
import { getAccounts } from '../../../utils/reducerUtils'; import { getAccounts } from '../../../utils/reducerUtils';
import { findSelectedDevice } from '../../../reducers/TrezorConnectReducer'; import { findSelectedDevice } from '../../../reducers/TrezorConnectReducer';
import Loader from '../../common/LoaderCircle'; import Loader from '../../common/LoaderCircle';
import Tooltip from 'rc-tooltip';
const AccountSelection = (props: any): any => { const AccountSelection = (props: any): any => {
@ -17,7 +19,11 @@ const AccountSelection = (props: any): any => {
const { location } = props.router; const { location } = props.router;
const accounts = props.accounts; const accounts = props.accounts;
const baseUrl: string = `/device/${location.params.device}`; const baseUrl: string = `/device/${location.params.device}`;
const fiatRate = props.fiatRate || '1';
const { config } = props.localStorage;
const selectedCoin = config.coins.find(c => c.network === location.params.coin);
const fiatRate = props.fiat.find(f => f.network === selectedCoin.network);
// console.warn("AccountSelectionRender", selected, props); // console.warn("AccountSelectionRender", selected, props);
@ -25,9 +31,18 @@ const AccountSelection = (props: any): any => {
let selectedAccounts = deviceAddresses.map((address, i) => { let selectedAccounts = deviceAddresses.map((address, i) => {
// const url: string = `${baseUrl}/coin/${location.params.coin}/address/${i}`; // const url: string = `${baseUrl}/coin/${location.params.coin}/address/${i}`;
const url: string = location.pathname.replace(/address+\/([0-9]*)/, `address/${i}`); const url: string = location.pathname.replace(/address+\/([0-9]*)/, `address/${i}`);
const b = new BigNumber(address.balance);
const fiat = b.times(fiatRate).toFixed(2); let balance: string = 'Loading...';
const balance = address.balance !== '' ? `${ address.balance } ${ location.params.coin.toUpperCase() } / $${ fiat }` : 'Loading...'; if (address.balance !== '') {
if (fiatRate) {
const accountBalance = new BigNumber(address.balance);
const fiat = accountBalance.times(fiatRate.value).toFixed(2);
balance = `${ address.balance } ${ selectedCoin.symbol } / $${ fiat }`;
} else {
balance = `${ address.balance } ${ selectedCoin.symbol }`;
}
}
return ( return (
<NavLink key={i} activeClassName="selected" className="account" to={ url }> <NavLink key={i} activeClassName="selected" className="account" to={ url }>
{ `Address #${(address.index + 1 )}` } { `Address #${(address.index + 1 )}` }
@ -54,34 +69,54 @@ const AccountSelection = (props: any): any => {
if (discovery) { if (discovery) {
if (discovery.completed) { if (discovery.completed) {
// TODO: add only if last one is not empty // TODO: add only if last one is not empty
discoveryStatus = ( //if (selectedAccounts.length > 0 && selectedAccounts[selectedAccounts.length - 1])
<div className="add-address" onClick={ props.addAddress } > const lastAccount = deviceAddresses[deviceAddresses.length - 1];
Add address if (lastAccount && new BigNumber(lastAccount.balance).greaterThan(0) || lastAccount.nonce > 0) {
</div> discoveryStatus = (
) <div className="add-address" onClick={ props.addAddress }>
Add address
</div>
);
} else {
const tooltip = (
<div className="aside-tooltip-wrapper">
To add a new address, last address must have some transactions.
</div>
)
discoveryStatus = (
<Tooltip
arrowContent={<div className="rc-tooltip-arrow-inner"></div>}
overlay={ tooltip }
placement="top">
<div className="add-address disabled">
Add address
</div>
</Tooltip>
);
}
} else if (!selected.connected) { } else if (!selected.connected) {
discoveryStatus = ( discoveryStatus = (
<div className="discovery-status"> <div className="discovery-status">
Addresses could not be loaded Addresses could not be loaded
<span>{ `Connect ${ selected.instanceLabel } device` }</span> <span>{ `Connect ${ selected.instanceLabel } device` }</span>
</div> </div>
) );
} else { } else {
discoveryStatus = ( discoveryStatus = (
<div className="discovery-loading"> <div className="discovery-loading">
<Loader size="20" /> Loading accounts... <Loader size="20" /> Loading accounts...
</div> </div>
) );
} }
} }
const { config } = props.localStorage;
const selectedCoin = config.coins.find(c => c.shortcut === location.params.coin);
let backButton = null; let backButton = null;
if (selectedCoin) { if (selectedCoin) {
backButton = ( backButton = (
<NavLink to={ baseUrl } className={ `back ${ selectedCoin.shortcut }` }> <NavLink to={ baseUrl } className={ `back ${ selectedCoin.network }` }>
<span className={ selectedCoin.shortcut }>{ selectedCoin.name }</span> <span className={ selectedCoin.network }>{ selectedCoin.name }</span>
</NavLink> </NavLink>
); );
} }
@ -89,7 +124,9 @@ const AccountSelection = (props: any): any => {
return ( return (
<section> <section>
{ backButton } { backButton }
{ selectedAccounts } <div>
{ selectedAccounts }
</div>
{ discoveryStatus } { discoveryStatus }
</section> </section>
); );

View File

@ -41,7 +41,7 @@ const Aside = (props: any): any => {
// return ( // return (
// <aside> // <aside>
// <div className="transition-container"></div> // <div className="transition-container"></div>
// <a className="help" href="https://trezor.io/support/" target="_blank"> // <a className="help" href="https://trezor.io/support/" target="_blank" rel="noreferrer noopener">
// Need help? // Need help?
// </a> // </a>
// </aside> // </aside>
@ -66,14 +66,12 @@ const Aside = (props: any): any => {
); );
} }
console.warn("ASIDEE", props)
return ( return (
<StickyContainer location={ location } devices={ props.deviceDropdownOpened.toString() }> <StickyContainer location={ location } devices={ props.deviceDropdownOpened.toString() }>
<DeviceSelect {...props} /> <DeviceSelect {...props} />
{ menu } { menu }
<div className="help"> <div className="help">
<a href="https://trezor.io/support/" target="_blank">Need help?</a> <a href="https://trezor.io/support/" target="_blank" rel="noreferrer noopener">Need help?</a>
</div> </div>
</StickyContainer> </StickyContainer>
) )

View File

@ -9,10 +9,10 @@ const CoinSelection = (props: any): any => {
const { config } = props.localStorage; const { config } = props.localStorage;
const walletCoins = config.coins.map(item => { const walletCoins = config.coins.map(item => {
const url = `${ location.pathname }/coin/${ item.shortcut }/address/0`; const url = `${ location.pathname }/coin/${ item.network }/address/0`;
const className = `coin ${ item.shortcut }` const className = `coin ${ item.network }`
return ( return (
<NavLink key={ item.shortcut } to={ url } className={ className }> <NavLink key={ item.network } to={ url } className={ className }>
{ item.name } { item.name }
</NavLink> </NavLink>
) )

View File

@ -70,7 +70,9 @@ const Value = (props: any): any => {
<span className="label">{ device.instanceLabel }</span> <span className="label">{ device.instanceLabel }</span>
<span className="status">{ deviceStatus }</span> <span className="status">{ deviceStatus }</span>
</div> </div>
{ deviceMenu } <div className="device-menu">
{ deviceMenuButtons }
</div>
</div> </div>
); );
} }

View File

@ -46,7 +46,7 @@ const AdvancedForm = (props: any): any => {
Gas Price is the amount you pay per unit of gas.<br/> Gas Price is the amount you pay per unit of gas.<br/>
<span>TX fee = gas price * gas limit</span> &amp; is paid to miners for including your TX in a block.<br/> <span>TX fee = gas price * gas limit</span> &amp; is paid to miners for including your TX in a block.<br/>
Higher the gas price = faster transaction, but more expensive. Default is <span>{ gasPrice } GWEI.</span><br/> Higher the gas price = faster transaction, but more expensive. Default is <span>{ gasPrice } GWEI.</span><br/>
<a className="green" href="https://myetherwallet.github.io/knowledge-base/gas/what-is-gas-ethereum.html" target="_blank">Read more</a> <a className="green" href="https://myetherwallet.github.io/knowledge-base/gas/what-is-gas-ethereum.html" target="_blank" rel="noreferrer noopener">Read more</a>
</div> </div>
); );
@ -123,8 +123,6 @@ const AdvancedForm = (props: any): any => {
{ props.children } { props.children }
</div> </div>
<ReactTooltip html={true} effect="solid" place="right" offset={{ bottom: 6, right: 24 }} />
</div> </div>
) )
} }

View File

@ -0,0 +1,67 @@
/* @flow */
'use strict';
import React from 'react';
import ColorHash from 'color-hash';
import ScaleText from 'react-scale-text';
const PendingTransactions = (props: any): any => {
const account = props.accounts.find(a => a.checksum === props.sendForm.checksum && a.index === props.sendForm.accountIndex && a.coin === props.sendForm.coin);
const pending = props.pending.filter(p => p.coin === account.coin && p.address === account.address);
if (pending.length < 1) return null;
const tokens = props.tokens.filter(t => t.ethAddress === account.address);
const bgColor = new ColorHash({lightness: 0.7});
const textColor = new ColorHash();
const pendings = pending.map((tx, i) => {
let iconColor, symbol, name;
if (tx.token !== tx.coin) {
const token = tokens.find(t => t.symbol === tx.token);
iconColor = {
color: textColor.hex(token.name),
background: bgColor.hex(token.name),
borderColor: bgColor.hex(token.name)
}
symbol = token.symbol.toUpperCase();
name = token.name;
} else {
iconColor = {
color: textColor.hex(tx.coin),
background: bgColor.hex(tx.coin),
borderColor: bgColor.hex(tx.coin)
}
symbol = props.selectedCoin.symbol;
name = props.selectedCoin.name;
}
return (
<div key={i} className="tx">
<div className="icon" style={ iconColor }>
<div className="icon-inner">
<ScaleText widthOnly><p>{ symbol }</p></ScaleText>
</div>
</div>
<div className="name">
<a href={ `${props.selectedCoin.explorer}/tx/${tx.id}`} target="_blank" rel="noreferrer noopener">{ name }</a>
</div>
<div className="amount">{ tx.amount } { symbol }</div>
</div>
)
});
return (
<div className="pending-transactions">
<h2>Pending transactions</h2>
{ pendings }
</div>
)
}
export default PendingTransactions;

View File

@ -4,6 +4,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import Select from 'react-select'; import Select from 'react-select';
import AdvancedForm from './AdvancedForm'; import AdvancedForm from './AdvancedForm';
import PendingTransactions from './PendingTransactions';
import { FeeSelectValue, FeeSelectOption } from './FeeSelect'; import { FeeSelectValue, FeeSelectOption } from './FeeSelect';
import { Notification } from '../../common/Notification'; import { Notification } from '../../common/Notification';
import AbstractAccount from '../account/AbstractAccount'; import AbstractAccount from '../account/AbstractAccount';
@ -27,6 +28,7 @@ const _render = (props: any): any => {
amount, amount,
setMax, setMax,
coin, coin,
coinSymbol,
token, token,
feeLevels, feeLevels,
fee, fee,
@ -38,7 +40,6 @@ const _render = (props: any): any => {
infos, infos,
advanced, advanced,
sending, sending,
sendingStatus
} = props.sendForm; } = props.sendForm;
const { const {
@ -51,11 +52,14 @@ const _render = (props: any): any => {
onSend, onSend,
} = props.sendFormActions; } = props.sendFormActions;
//const addressTokens = props.tokens.filter(t => t.ethAddress === currentAccount.address); const { config } = props.localStorage;
const selectedCoin = config.coins.find(c => c.network === coin);
const fiatRate = props.fiat.find(f => f.network === selectedCoin.network);
const tokens = addressTokens.map(t => { const tokens = addressTokens.map(t => {
return { value: t.symbol, label: t.symbol }; return { value: t.symbol, label: t.symbol };
}); });
tokens.unshift({ value: coin, label: coin.toUpperCase() }); tokens.unshift({ value: selectedCoin.network, label: selectedCoin.symbol });
const setMaxClassName: string = setMax ? 'set-max enabled' : 'set-max'; const setMaxClassName: string = setMax ? 'set-max enabled' : 'set-max';
@ -75,12 +79,12 @@ const _render = (props: any): any => {
addressClassName = 'valid'; addressClassName = 'valid';
} }
let buttonDisabled: boolean = Object.keys(errors).length > 0 || total === '0' || address.length === 0 || sending; let buttonDisabled: boolean = Object.keys(errors).length > 0 || total === '0' || amount.length === 0 || address.length === 0 || sending;
let buttonLabel: string = 'Send'; let buttonLabel: string = 'Send';
if (coin !== token && amount.length > 0 && !errors.amount) { if (coin !== token && amount.length > 0 && !errors.amount) {
buttonLabel += ` ${amount} ${ token.toUpperCase() }` buttonLabel += ` ${amount} ${ token.toUpperCase() }`
} else if (coin === token && total !== '0') { } else if (coin === token && total !== '0') {
buttonLabel += ` ${total} ${ token.toUpperCase() }`; buttonLabel += ` ${total} ${ selectedCoin.symbol }`;
} }
//const device = props.devices.find(d => d.checksum === currentAccount.checksum); //const device = props.devices.find(d => d.checksum === currentAccount.checksum);
@ -90,13 +94,6 @@ const _render = (props: any): any => {
} }
let notification = null; let notification = null;
// if (sendingStatus) {
// if (sendingStatus.success) {
// notification = (<Notification className="success" title="Transaction sent" message={ sendingStatus.message } />);
// } else {
// notification = (<Notification className="error" title="Transaction error" message={ sendingStatus.message } />);
// }
// }
return ( return (
<section className="send-form"> <section className="send-form">
@ -171,6 +168,8 @@ const _render = (props: any): any => {
<AdvancedForm { ...props}> <AdvancedForm { ...props}>
<button disabled={ buttonDisabled } onClick={ event => onSend() }>{ buttonLabel }</button> <button disabled={ buttonDisabled } onClick={ event => onSend() }>{ buttonLabel }</button>
</AdvancedForm> </AdvancedForm>
<PendingTransactions {...props} selectedCoin={selectedCoin} />
</section> </section>
); );

View File

@ -31,42 +31,45 @@ const _render = (props: any): any => {
<Notification className="info" title={ `Device ${ device.instanceLabel } is disconnected` } /> <Notification className="info" title={ `Device ${ device.instanceLabel } is disconnected` } />
) : null } ) : null }
<h2 className={ props.summary.coin }>Address #{ parseInt(props.match.params.address) + 1 }</h2> <h2 className={ `summary-header ${props.summary.coin}` }>Address #{ parseInt(props.match.params.address) + 1 }</h2>
<SummaryDetails <SummaryDetails
summary={ props.summary } summary={ props.summary }
balance={ account.balance } balance={ account.balance }
coin={ props.summary.coin } coin={ props.summary.coin }
fiatRate={ props.fiatRate } fiat={ props.fiat }
localStorage={ props.localStorage }
onToggle={ props.summaryActions.onDetailsToggle } /> onToggle={ props.summaryActions.onDetailsToggle } />
<h2>Tokens</h2>
{/* 0x58cda554935e4a1f2acbe15f8757400af275e084 */}
<div className="filter"> <div className="filter">
0x58cda554935e4a1f2acbe15f8757400af275e084
<Async <Async
className="token-select" className="token-select"
multi={ false } multi={ false }
autoload={ false } autoload={ false }
ignoreCase={ true } ignoreCase={ true }
filterOptions= { filterOptions= {
(opt, str, values) => { (options, search, values) => {
console.log("TODO: filter already added", opt, str, values); return options.filter(o => {
return opt; return !tokens.find(t => t.symbol === o.symbol);
});
} }
} }
value={ props.summary.selectedToken } value={ props.summary.selectedToken }
onChange={ token => props.summaryActions.selectToken(token, account) } onChange={ token => props.summaryActions.selectToken(token, account) }
valueKey="symbol" valueKey="symbol"
labelKey="symbol" labelKey="symbol"
placeholder="Search for token" placeholder="Search for token"
loadOptions={ props.summaryActions.loadTokens } searchPromptText="Type token name or address"
noResultsText="Token not found"
loadOptions={ input => props.summaryActions.loadTokens(input, account) }
backspaceRemoves={true} /> backspaceRemoves={true} />
</div> </div>
<SummaryTokens tokens={ tokens } /> <SummaryTokens tokens={ tokens } removeToken={ props.summaryActions.removeToken } />
</section> </section>

View File

@ -16,7 +16,8 @@ function mapStateToProps(state, own) {
discovery: state.discovery, discovery: state.discovery,
tokens: state.tokens, tokens: state.tokens,
summary: state.summary, summary: state.summary,
fiatRate: state.web3.fiatRate fiat: state.fiat,
localStorage: state.localStorage
}; };
} }

View File

@ -2,34 +2,60 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import BigNumber from 'bignumber.js';
const SummaryDetails = (props: any): any => { const SummaryDetails = (props: any): any => {
if (!props.summary.details) { if (!props.summary.details) return (
return ( <div className="summary-details">
<div className="summary-details closed"> <div className="toggle" onClick={ props.onToggle }></div>
<div className="toggle" onClick={ props.onToggle }></div> </div>
);
const { config } = props.localStorage;
const selectedCoin = config.coins.find(c => c.network === props.coin);
const fiatRate = props.fiat.find(f => f.network === selectedCoin.network);
let balanceColumn = null;
let rateColumn = null;
if (fiatRate) {
const accountBalance = new BigNumber(props.balance);
const fiatValue = new BigNumber(fiatRate.value);
const fiat = accountBalance.times(fiatValue).toFixed(2);
balanceColumn = (
<div className="column">
<div className="label">Balance</div>
<div className="fiat-value">${ fiat }</div>
<div className="value">{ props.balance } { selectedCoin.symbol }</div>
</div>
);
rateColumn = (
<div className="column">
<div className="label">Rate</div>
<div className="fiat-value">${ fiatValue.toFixed(2) }</div>
<div className="value">1.00 { selectedCoin.symbol }</div>
</div> </div>
) )
} else {
balanceColumn = (
<div className="column">
<div className="label">Balance</div>
<div className="fiat-value">{ props.balance } { selectedCoin.symbol }</div>
</div>
);
} }
const fiatValue = "0";
return ( return (
<div className="summary-details"> <div className="summary-details opened">
<div className="content">
<div className="column">
<div className="label">Balance</div>
<div className="fiat-value">${ fiatValue }</div>
<div className="value">{ props.balance } ETH</div>
</div>
<div className="column">
<div className="label">Rate</div>
<div className="fiat-value">${ props.fiatRate }</div>
<div className="value">1.00 ETH</div>
</div>
</div>
<div className="toggle" onClick={ props.onToggle }></div> <div className="toggle" onClick={ props.onToggle }></div>
<div className="content">
{ balanceColumn }
{ rateColumn }
</div>
</div> </div>
); );
} }

View File

@ -13,10 +13,6 @@ const SummaryTokens = (props: any): any => {
const textColor = new ColorHash(); const textColor = new ColorHash();
const tokens = props.tokens.map((t, i) => { const tokens = props.tokens.map((t, i) => {
// if (search.length > 0) {
// if (t.name.toLowerCase().indexOf(search) < 0 && t.shortcut.toLowerCase().indexOf(search) < 0) return null;
// }
let iconColor = { let iconColor = {
color: textColor.hex(t.name), color: textColor.hex(t.name),
background: bgColor.hex(t.name), background: bgColor.hex(t.name),
@ -30,7 +26,8 @@ const SummaryTokens = (props: any): any => {
</div> </div>
</div> </div>
<div className="name">{ t.name }</div> <div className="name">{ t.name }</div>
<div className="balance">{ t.balance }</div> <div className="balance">{ t.balance } { t.symbol }</div>
<button className="transparent" onClick={ event => props.removeToken(t) }></button>
</div> </div>
) )
}); });

View File

@ -15,7 +15,7 @@ function mapStateToProps(state, own) {
accounts: state.accounts, accounts: state.accounts,
router: state.router, router: state.router,
deviceDropdownOpened: state.DOM.deviceDropdownOpened, deviceDropdownOpened: state.DOM.deviceDropdownOpened,
fiatRate: state.web3.fiatRate, fiat: state.fiat,
localStorage: state.localStorage, localStorage: state.localStorage,
discovery: state.discovery discovery: state.discovery
}; };

View File

@ -13,6 +13,7 @@ import Footer from '../components/common/Footer';
import AccountTabs from '../components/wallet/account/AccountTabs'; import AccountTabs from '../components/wallet/account/AccountTabs';
import * as TrezorConnectActions from '../actions/TrezorConnectActions'; import * as TrezorConnectActions from '../actions/TrezorConnectActions';
import * as LogActions from '../actions/LogActions';
const Article = (props) => { const Article = (props) => {
return ( return (
@ -20,8 +21,8 @@ const Article = (props) => {
<nav> <nav>
<Route path="/device/:device/coin/:coin/address/:address" component={ AccountTabs } /> <Route path="/device/:device/coin/:coin/address/:address" component={ AccountTabs } />
</nav> </nav>
{/* <Log /> */}
<Notifications /> <Notifications />
<Log />
{ props.children } { props.children }
<Footer /> <Footer />
</article> </article>
@ -30,13 +31,13 @@ const Article = (props) => {
function mapStateToProps(state, own) { function mapStateToProps(state, own) {
return { return {
}; };
} }
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
return { return {
}; };
} }

View File

@ -6,6 +6,7 @@ import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import LandingPage from '../components/landing/LandingPage'; import LandingPage from '../components/landing/LandingPage';
import * as LogActions from '../actions/LogActions';
function mapStateToProps(state, own) { function mapStateToProps(state, own) {
return { return {
@ -19,6 +20,7 @@ function mapStateToProps(state, own) {
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
return { return {
}; };
} }

View File

@ -15,8 +15,10 @@ function mapStateToProps(state, own) {
accounts: state.accounts, accounts: state.accounts,
discovery: state.discovery, discovery: state.discovery,
tokens: state.tokens, tokens: state.tokens,
pending: state.pending,
sendForm: state.sendForm, sendForm: state.sendForm,
fiatRate: state.web3.fiatRate fiat: state.fiat,
localStorage: state.localStorage
}; };
} }

8
src/js/flowtype/index.js Normal file
View File

@ -0,0 +1,8 @@
/* @flow */
'use strict';
export type Dispatch = (action: Action | ThunkAction | PromiseAction) => any;
export type GetState = () => State;
export type ThunkAction = (dispatch: Dispatch, getState: GetState) => any;
export type PromiseAction = Promise<Action>;

View File

@ -7,18 +7,18 @@ import store from './store';
import router from './router'; import router from './router';
import { onResize, onBeforeUnload } from './actions/AppActions'; import { onResize, onBeforeUnload } from './actions/AppActions';
import Raven from 'raven-js';
import styles from '../styles/index.less'; import styles from '../styles/index.less';
Raven.config('https://497392c3ff6e46dc9e54eef123979378@sentry.io/294339').install();
render( render(
router, router,
document.getElementById('root') document.getElementById('root')
); );
// handle resize event and pass it to DOM reducer
window.addEventListener('resize', () => {
store.dispatch( onResize() );
});
window.onbeforeunload = () => { window.onbeforeunload = () => {
store.dispatch( onBeforeUnload() ); store.dispatch( onBeforeUnload() );
} }

View File

@ -0,0 +1,37 @@
/* @flow */
'use strict';
export type Fiat = {
+network: string;
value: string;
}
export const initialState: Array<Fiat> = [];
const update = (state: Array<Fiat>, action: any): Array<Fiat> => {
const newState: Array<Fiat> = [ ...state ];
const exists: ?Fiat = newState.find(f => f.network === action.network);
if (exists) {
exists.value = action.rate.price_usd;
} else {
newState.push({
network: action.network,
value: action.rate.price_usd
})
}
return newState;
}
export default (state: Array<Fiat> = initialState, action: any): Array<Fiat> => {
switch (action.type) {
case 'rate__update' :
return update(state, action);
default:
return state;
}
}

View File

@ -0,0 +1,46 @@
/* @flow */
'use strict';
type LogEntry = {
time: number;
type: string;
messgage: string;
}
export type State = {
opened: boolean;
entries: Array<LogEntry>;
}
export const initialState: State = {
opened: false,
entries: [],
};
export default (state: State = initialState, action: any): State => {
switch (action.type) {
case 'log__open':
return {
...state,
opened: true
}
case 'log__close':
return {
...state,
opened: false
}
case 'log__add':
return {
...state,
}
default:
return state;
}
}

View File

@ -0,0 +1,54 @@
/* @flow */
'use strict';
import * as SEND from '../actions/constants/SendForm';
import * as WEB3 from '../actions/constants/Web3';
export type PendingTx = {
+id: string;
+coin: string;
+token: string;
+amount: string;
+address: string;
}
const initialState: Array<PendingTx> = [];
const add = (state: Array<PendingTx>, action: any) => {
const newState = [ ...state ];
newState.push({
id: action.txid,
coin: action.address.coin,
token: action.token,
amount: action.amount,
address: action.address.address,
});
return newState;
}
const remove = (state: Array<PendingTx>, action: any) => {
return state.filter(tx => tx.id !== action.tx.id);
}
const fromStorage = (state: Array<PendingTx>, action: any) => {
return state.filter(tx => tx.id !== action.tx.id);
}
export default function pending(state: Array<PendingTx> = initialState, action: any): any {
switch (action.type) {
case SEND.TX_COMPLETE :
return add(state, action);
case WEB3.PENDING_TX_RESOLVED :
return remove(state, action);
case 'PENDING.FROM_STORAGE' :
return action.payload;
default:
return state;
}
}

View File

@ -13,6 +13,7 @@ export type State = {
+checksum: ?string; +checksum: ?string;
+accountIndex: number; +accountIndex: number;
+coin: string; +coin: string;
+coinSymbol: string;
token: string; token: string;
location: string; location: string;
@ -36,7 +37,6 @@ export type State = {
nonce: string; nonce: string;
total: string; total: string;
sending: boolean; sending: boolean;
sendingStatus: ?SendStatus;
errors: {[k: string]: string}; errors: {[k: string]: string};
warnings: {[k: string]: string}; warnings: {[k: string]: string};
infos: {[k: string]: string}; infos: {[k: string]: string};
@ -48,15 +48,12 @@ export type FeeLevel = {
value: string; value: string;
} }
type SendStatus = {
success: boolean;
message: string;
}
export const initialState: State = { export const initialState: State = {
checksum: null, checksum: null,
accountIndex: 0, accountIndex: 0,
coin: '', coin: '',
coinSymbol: '',
token: '', token: '',
location: '', location: '',
@ -64,8 +61,8 @@ export const initialState: State = {
untouched: true, untouched: true,
touched: {}, touched: {},
balanceNeedUpdate: false, balanceNeedUpdate: false,
//address: '', address: '',
address: '0x574BbB36871bA6b78E27f4B4dCFb76eA0091880B', //address: '0x574BbB36871bA6b78E27f4B4dCFb76eA0091880B',
amount: '', amount: '',
setMax: false, setMax: false,
feeLevels: [], feeLevels: [],
@ -78,7 +75,6 @@ export const initialState: State = {
nonce: '0', nonce: '0',
total: '0', total: '0',
sending: false, sending: false,
sendingStatus: null,
errors: {}, errors: {},
warnings: {}, warnings: {},
infos: {}, infos: {},
@ -166,26 +162,32 @@ export default (state: State = initialState, action: any): State => {
return { return {
...state, ...state,
sending: true, sending: true,
sendingStatus: null,
} }
case SEND.TX_COMPLETE : case SEND.TX_COMPLETE :
return { return {
...state, ...state,
sending: false, sending: false,
sendingStatus: { touched: {},
success: true, address: '',
message: action.txid amount: '',
} setMax: false,
gasPriceNeedsUpdate: false,
gasLimit: state.gasLimit,
gasPrice: state.recommendedGasPrice,
data: '',
nonce: '0',
total: '0',
errors: {},
warnings: {},
infos: {},
} }
case SEND.TX_ERROR : case SEND.TX_ERROR :
return { return {
...state, ...state,
sending: false, sending: false,
sendingStatus: {
success: false,
message: action.response
}
} }

View File

@ -47,6 +47,12 @@ const forget = (state: Array<Token>, action: any): Array<Token> => {
return state.filter(t => t.checksum !== action.device.checksum); return state.filter(t => t.checksum !== action.device.checksum);
} }
const remove = (state: Array<Token>, action: any): Array<Token> => {
return state.filter(t => {
return !(t.ethAddress === action.token.ethAddress && t.address === action.token.address);
});
}
export default (state: Array<Token> = initialState, action: any): Array<Token> => { export default (state: Array<Token> = initialState, action: any): Array<Token> => {
switch (action.type) { switch (action.type) {
@ -54,6 +60,9 @@ export default (state: Array<Token> = initialState, action: any): Array<Token> =
case TOKEN.ADD : case TOKEN.ADD :
return create(state, action.payload); return create(state, action.payload);
case TOKEN.REMOVE :
return remove(state, action);
case TOKEN.SET_BALANCE : case TOKEN.SET_BALANCE :
return setBalance(state, action.payload); return setBalance(state, action.payload);

View File

@ -5,7 +5,6 @@ import { TRANSPORT, DEVICE } from 'trezor-connect';
import * as CONNECT from '../actions/constants/TrezorConnect'; import * as CONNECT from '../actions/constants/TrezorConnect';
export type TrezorDevice = { export type TrezorDevice = {
initialized: boolean;
remember: boolean; remember: boolean;
connected: boolean; connected: boolean;
path: string; path: string;
@ -22,7 +21,7 @@ export type TrezorDevice = {
} }
export type SelectedDevice = { export type SelectedDevice = {
id: string; id: string; // could be device path if unacquired or features.device_id
instance: ?number; instance: ?number;
} }
@ -74,7 +73,6 @@ const mergeDevices = (current: TrezorDevice, upcoming: Object): TrezorDevice =>
// ...current, // ...current,
...upcoming, ...upcoming,
// make sure that instance specific variables will not be overridden // make sure that instance specific variables will not be overridden
initialized: current.initialized,
connected: typeof upcoming.connected === 'boolean' ? upcoming.connected : current.connected, connected: typeof upcoming.connected === 'boolean' ? upcoming.connected : current.connected,
remember: typeof upcoming.remember === 'boolean' ? upcoming.remember : current.remember, remember: typeof upcoming.remember === 'boolean' ? upcoming.remember : current.remember,
instance: current.instance, instance: current.instance,
@ -106,12 +104,11 @@ const addDevice = (state: State, device: Object): State => {
let affectedDevices: Array<TrezorDevice> = []; let affectedDevices: Array<TrezorDevice> = [];
let otherDevices: Array<TrezorDevice> = []; let otherDevices: Array<TrezorDevice> = [];
if (device.unacquired) { if (device.unacquired) {
// connected device is unacquired, but it was already merged with saved devices // check if connected device is unacquired, but it was already merged with saved device(s) after DEVICE.CHANGE action
// when DEVICE.CHANGE event occurs
// ignore this event
affectedDevices = newState.devices.filter(d => d.path === device.path); affectedDevices = newState.devices.filter(d => d.path === device.path);
const diff = newState.devices.filter(d => affectedDevices.indexOf(d) === -1); const diff = newState.devices.filter(d => affectedDevices.indexOf(d) === -1);
// if so, ignore this action
if (affectedDevices.length > 0) { if (affectedDevices.length > 0) {
return state; return state;
} }
@ -129,13 +126,11 @@ const addDevice = (state: State, device: Object): State => {
const newDevice: TrezorDevice = { const newDevice: TrezorDevice = {
...device, ...device,
initialized: false,
acquiring: false, acquiring: false,
remember: false, remember: false,
connected: true, connected: true,
path: device.path, path: device.path,
label: device.label, label: device.label,
id: 'ABCD',
checksum: null, checksum: null,
// instance: 0, // instance: 0,
instanceLabel: device.label, instanceLabel: device.label,
@ -158,7 +153,6 @@ const setDeviceState = (state: State, action: any): State => {
if (index > -1) { if (index > -1) {
const changedDevice: TrezorDevice = { const changedDevice: TrezorDevice = {
...newState.devices[index], ...newState.devices[index],
initialized: true,
checksum: action.checksum checksum: action.checksum
}; };
newState.devices[index] = changedDevice; newState.devices[index] = changedDevice;
@ -251,7 +245,8 @@ const forgetDevice = (state: State, action: any): State => {
newState.devices.splice(newState.devices.indexOf(action.device), 1); newState.devices.splice(newState.devices.indexOf(action.device), 1);
} else { } else {
// remove all instances after disconnect (remember request declined) // remove all instances after disconnect (remember request declined)
newState.devices = state.devices.filter(d => d.path !== action.device.path); //newState.devices = state.devices.filter(d => d.path !== action.device.path);
newState.devices = state.devices.filter(d => (d.features && d.features.device_id !== action.device.features.device_id) || (!d.features && d.path !== action.device.path));
} }
return newState; return newState;
@ -277,13 +272,11 @@ const duplicate = (state: State, device: any): State => {
// if (affectedDevices.length > 0) { // if (affectedDevices.length > 0) {
const newDevice: TrezorDevice = { const newDevice: TrezorDevice = {
...device, ...device,
initialized: false,
checksum: null, checksum: null,
remember: device.remember, remember: device.remember,
connected: device.connected, connected: device.connected,
path: device.path, path: device.path,
label: device.label, label: device.label,
id: 'ABCD',
instance: new Date().getTime(), instance: new Date().getTime(),
instanceLabel: device.instanceLabel, instanceLabel: device.instanceLabel,
ts: 0, ts: 0,

View File

@ -5,6 +5,7 @@ import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux'; import { routerReducer } from 'react-router-redux';
import DOM from './AppReducer.js'; import DOM from './AppReducer.js';
import log from './LogReducer.js';
import localStorage from './LocalStorageReducer.js'; import localStorage from './LocalStorageReducer.js';
import connect from './TrezorConnectReducer.js'; import connect from './TrezorConnectReducer.js';
import notifications from './NotificationReducer.js'; import notifications from './NotificationReducer.js';
@ -17,10 +18,13 @@ import receive from './ReceiveReducer.js';
import summary from './SummaryReducer.js'; import summary from './SummaryReducer.js';
import tokens from './TokensReducer.js'; import tokens from './TokensReducer.js';
import discovery from './DiscoveryReducer.js'; import discovery from './DiscoveryReducer.js';
import pending from './PendingTxReducer.js';
import fiat from './FiatRateReducer.js';
export default combineReducers({ export default combineReducers({
router: routerReducer, router: routerReducer,
DOM, DOM,
log,
localStorage, localStorage,
connect, connect,
notifications, notifications,
@ -32,5 +36,7 @@ export default combineReducers({
receive, receive,
summary, summary,
tokens, tokens,
discovery discovery,
pending,
fiat
}); });

View File

@ -7,13 +7,17 @@ import { resolveAfter } from '../utils/promiseUtils';
const loadRateAction = (): any => { const loadRateAction = (): any => {
return async (dispatch, getState) => { return async (dispatch, getState) => {
const config = getState().localStorage.config;
try { try {
const rate = await httpRequest('https://api.coinmarketcap.com/v1/ticker/ethereum/?convert=USD', 'json');
dispatch({ for (let i = 0; i < config.fiatValueTickers.length; i++) {
type: 'rate__update', const rate = await httpRequest(`${config.fiatValueTickers[i].url}?convert=USD`, 'json');
rate: rate[0] dispatch({
}) type: 'rate__update',
network: config.fiatValueTickers[i].network,
rate: rate[0]
});
}
} catch(error) { } catch(error) {
@ -29,11 +33,12 @@ const loadRateAction = (): any => {
*/ */
const LocalStorageService = (store: any) => (next: any) => (action: any) => { const LocalStorageService = (store: any) => (next: any) => (action: any) => {
if (action.type === LOCATION_CHANGE && !store.getState().router.location) { next(action);
//if (action.type === LOCATION_CHANGE && !store.getState().router.location) {
if (action.type === 'storage__ready') {
store.dispatch(loadRateAction()); store.dispatch(loadRateAction());
} }
next(action);
}; };
export default LocalStorageService; export default LocalStorageService;

View File

@ -10,6 +10,8 @@ import * as MODAL from '../actions/constants/Modal';
import * as TOKEN from '../actions/constants/Token'; import * as TOKEN from '../actions/constants/Token';
import * as ADDRESS from '../actions/constants/Address'; import * as ADDRESS from '../actions/constants/Address';
import * as DISCOVERY from '../actions/constants/Discovery'; import * as DISCOVERY from '../actions/constants/Discovery';
import * as SEND from '../actions/constants/SendForm';
import * as WEB3 from '../actions/constants/Web3';
// https://github.com/STRML/react-localstorage/blob/master/react-localstorage.js // https://github.com/STRML/react-localstorage/blob/master/react-localstorage.js
@ -34,10 +36,17 @@ const findDiscovery = (devices, discovery) => {
}, []); }, []);
} }
const findPendingTxs = (accounts, pending) => {
return accounts.reduce((arr, account) => {
return arr.concat(pending.filter(a => a.address === account.address));
}, []);
}
const save = (dispatch, getState) => { const save = (dispatch, getState) => {
const devices = getState().connect.devices.filter(d => d.remember === true && !d.unacquired); const devices = getState().connect.devices.filter(d => d.remember === true && !d.unacquired);
const accounts = findAccounts(devices, getState().accounts); const accounts = findAccounts(devices, getState().accounts);
const tokens = findTokens(accounts, getState().tokens); const tokens = findTokens(accounts, getState().tokens);
const pending = findPendingTxs(accounts, getState().pending);
const discovery = findDiscovery(devices, getState().discovery); const discovery = findDiscovery(devices, getState().discovery);
// save devices // save devices
@ -51,6 +60,9 @@ const save = (dispatch, getState) => {
// tokens // tokens
dispatch( LocalStorageActions.save('tokens', JSON.stringify( tokens ) ) ); dispatch( LocalStorageActions.save('tokens', JSON.stringify( tokens ) ) );
// pending transactions
dispatch( LocalStorageActions.save('pending', JSON.stringify( pending ) ) );
} }
@ -74,6 +86,7 @@ const LocalStorageService = (store: any) => (next: any) => (action: any) => {
break; break;
case TOKEN.ADD : case TOKEN.ADD :
case TOKEN.REMOVE :
case TOKEN.SET_BALANCE : case TOKEN.SET_BALANCE :
save(store.dispatch, store.getState); save(store.dispatch, store.getState);
// store.dispatch( LocalStorageActions.save('tokens', JSON.stringify( tokens ) ) ); // store.dispatch( LocalStorageActions.save('tokens', JSON.stringify( tokens ) ) );
@ -103,6 +116,11 @@ const LocalStorageService = (store: any) => (next: any) => (action: any) => {
// store.dispatch( LocalStorageActions.save('selectedDevice', JSON.stringify( store.getState().connect.selectedDevice ) ) ); // store.dispatch( LocalStorageActions.save('selectedDevice', JSON.stringify( store.getState().connect.selectedDevice ) ) );
break; break;
case SEND.TX_COMPLETE :
case WEB3.PENDING_TX_RESOLVED :
save(store.dispatch, store.getState);
break;
} }

View File

@ -38,7 +38,7 @@ const validation = (store: any, params: UrlParams): boolean => {
if (params.hasOwnProperty('device')) { if (params.hasOwnProperty('device')) {
const { devices } = store.getState().connect; const { devices } = store.getState().connect;
let device; // = devices.find(d => d.path === params.device || d.features.device_id === params.device); let device;
if (params.hasOwnProperty('deviceInstance')) { if (params.hasOwnProperty('deviceInstance')) {
device = devices.find(d => d.features && d.features.device_id === params.device && d.instance === params.deviceInstance ); device = devices.find(d => d.features && d.features.device_id === params.device && d.instance === params.deviceInstance );
} else { } else {
@ -50,7 +50,7 @@ const validation = (store: any, params: UrlParams): boolean => {
if (params.hasOwnProperty('coin')) { if (params.hasOwnProperty('coin')) {
const { config } = store.getState().localStorage; const { config } = store.getState().localStorage;
const coin = config.coins.find(c => c.symbol === params.coin); const coin = config.coins.find(c => c.network === params.coin);
if (!coin) return false; if (!coin) return false;
if (!params.address) return false; if (!params.address) return false;
} }

View File

@ -16,7 +16,7 @@ import * as ACTIONS from '../actions';
const TrezorConnectService = (store: any) => (next: any) => (action: any) => { const TrezorConnectService = (store: any) => (next: any) => (action: any) => {
const prevState = store.getState().connect; const prevState = store.getState().connect;
const prevModalState = store.getState().connect; const prevModalState = store.getState().modal;
next(action); next(action);
@ -32,6 +32,22 @@ const TrezorConnectService = (store: any) => (next: any) => (action: any) => {
} else if (action.type === DEVICE.DISCONNECT) { } else if (action.type === DEVICE.DISCONNECT) {
store.dispatch( TrezorConnectActions.deviceDisconnect(action.device) ); store.dispatch( TrezorConnectActions.deviceDisconnect(action.device) );
} else if (action.type === CONNECT.REMEMBER_REQUEST) {
// TODO:
if (prevModalState.opened && prevModalState.windowType === CONNECT.REMEMBER_REQUEST) {
store.dispatch({
type: CONNECT.FORGET,
device: store.getState().modal.device
});
store.dispatch({
type: CONNECT.FORGET,
device: prevModalState.device
});
}
} else if (action.type === CONNECT.FORGET) { } else if (action.type === CONNECT.FORGET) {
//store.dispatch( TrezorConnectActions.forgetDevice(action.device) ); //store.dispatch( TrezorConnectActions.forgetDevice(action.device) );
store.dispatch( TrezorConnectActions.switchToFirstAvailableDevice() ); store.dispatch( TrezorConnectActions.switchToFirstAvailableDevice() );
@ -62,6 +78,7 @@ const TrezorConnectService = (store: any) => (next: any) => (action: any) => {
// interrupt process of remembering device (force forget) // interrupt process of remembering device (force forget)
// TODO: the same for disconnect more than 1 device at once // TODO: the same for disconnect more than 1 device at once
// TODO: move it to modal actions
const { modal } = store.getState(); const { modal } = store.getState();
if (modal.opened && modal.windowType === CONNECT.REMEMBER_REQUEST) { if (modal.opened && modal.windowType === CONNECT.REMEMBER_REQUEST) {
if (action.device.features && modal.device.features.device_id === action.device.features.device_id) { if (action.device.features && modal.device.features.device_id === action.device.features.device_id) {

View File

@ -12,6 +12,7 @@ import reducers from '../reducers';
import services from '../services'; import services from '../services';
import { Middleware } from 'redux'; import { Middleware } from 'redux';
import { GenericStoreEnhancer } from 'redux'; import { GenericStoreEnhancer } from 'redux';
import RavenMiddleware from 'redux-raven-middleware';
export const history = createHistory( { queryKey: false } ); export const history = createHistory( { queryKey: false } );
@ -19,6 +20,7 @@ const initialState: any = {};
const enhancers = []; const enhancers = [];
const middleware = [ const middleware = [
thunk, thunk,
RavenMiddleware('https://497392c3ff6e46dc9e54eef123979378@sentry.io/294339'),
routerMiddleware(history) routerMiddleware(history)
]; ];

View File

@ -0,0 +1,137 @@
pragma solidity ^0.4.4;
contract Token {
/// @return total amount of tokens
function totalSupply() constant returns (uint256 supply) {}
/// @param _owner The address from which the balance will be retrieved
/// @return The balance
function balanceOf(address _owner) constant returns (uint256 balance) {}
/// @notice send `_value` token to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transfer(address _to, uint256 _value) returns (bool success) {}
/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {}
/// @notice `msg.sender` approves `_addr` to spend `_value` tokens
/// @param _spender The address of the account able to transfer the tokens
/// @param _value The amount of wei to be approved for transfer
/// @return Whether the approval was successful or not
function approve(address _spender, uint256 _value) returns (bool success) {}
/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to spent
function allowance(address _owner, address _spender) constant returns (uint256 remaining) {}
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
contract StandardToken is Token {
function transfer(address _to, uint256 _value) returns (bool success) {
//Default assumes totalSupply can't be over max (2^256 - 1).
//If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap.
//Replace the if with this one instead.
//if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
if (balances[msg.sender] >= _value && _value > 0) {
balances[msg.sender] -= _value;
balances[_to] += _value;
Transfer(msg.sender, _to, _value);
return true;
} else { return false; }
}
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
//same as above. Replace this line with the following if you want to protect against wrapping uints.
//if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) {
balances[_to] += _value;
balances[_from] -= _value;
allowed[_from][msg.sender] -= _value;
Transfer(_from, _to, _value);
return true;
} else { return false; }
}
function balanceOf(address _owner) constant returns (uint256 balance) {
return balances[_owner];
}
function approve(address _spender, uint256 _value) returns (bool success) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
return allowed[_owner][_spender];
}
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
uint256 public totalSupply;
}
//name this contract whatever you'd like
contract DariaCoin is StandardToken {
function () {
//if ether is sent to this address, send it back.
throw;
}
/* Public variables of the token */
/*
NOTE:
The following variables are OPTIONAL vanities. One does not have to include them.
They allow one to customise the token contract & in no way influences the core functionality.
Some wallets/interfaces might not even bother to look at this information.
*/
string public name; //fancy name: eg Simon Bucks
uint8 public decimals; //How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether.
string public symbol; //An identifier: eg SBX
string public version = 'H1.0'; //human 0.1 standard. Just an arbitrary versioning scheme.
//
// CHANGE THESE VALUES FOR YOUR TOKEN
//
//make sure this function name matches the contract name above. So if you're token is called TutorialToken, make sure the //contract name above is also TutorialToken instead of ERC20Token
function DariaCoin(
) {
balances[msg.sender] = 666; // Give the creator all initial tokens (100000 for example)
totalSupply = 666; // Update total supply (100000 for example)
name = "Daria Coin :)"; // Set the name for display purposes
decimals = 2; // Amount of decimals for display purposes
symbol = "DC!"; // Set the symbol for display purposes
}
/* Approves and then calls the receiving contract */
function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
//call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this.
//receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData)
//it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead.
if(!_spender.call(bytes4(bytes32(sha3("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData)) { throw; }
return true;
}
}

View File

@ -11,6 +11,7 @@ aside {
width: 320px; width: 320px;
height: 64px; height: 64px;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.04); box-shadow: 0 3px 8px rgba(0, 0, 0, 0.04);
background: @color_white;
.Select-control { .Select-control {
height: 63px; height: 63px;
@ -43,9 +44,9 @@ aside {
.Select-arrow { .Select-arrow {
visibility: hidden; visibility: hidden;
&:after { // &:after {
content: '' // content: ''
} // }
} }
.device { .device {
@ -197,10 +198,10 @@ aside {
color: @color_text_primary; color: @color_text_primary;
height: 50px; height: 50px;
.hover(); transition: background-color 0.3s, color 0.3s;
&:hover { &:hover {
background: @color_gray_light; background-color: @color_gray_light;
} }
&.account { &.account {
@ -216,21 +217,49 @@ aside {
color: @color_text_secondary; color: @color_text_secondary;
} }
// &:last-child { &:before {
// border-bottom: 1px solid @color_divider; content: '';
// } width: 0px;
} height: 100%;
position: absolute;
top: 0;
left: 0;
background: @color_green_primary;
transition: width 0.3s;
}
&.selected { &.selected {
background: @color_white;
border-left: 3px solid @color_green_primary;
padding-left: 27px;
&:hover {
background: @color_white; background: @color_white;
//border-left: 3px solid @color_green_primary;
//border-bottom: 1px solid @color_divider;
//padding-left: 27px;
&:before {
width: 3px;
}
&:hover {
background: @color_white;
}
&:last-child {
// border-bottom: 1px solid @color_divider;
&:after {
content: '';
position: absolute;
bottom: 0px;
left: 0px;
width: 100%;
height: 1px;
line-height: 1px;
background-color: @color_divider;
}
}
} }
} }
&.coin { &.coin {
padding-left: 80px; padding-left: 80px;
&:before { &:before {
@ -303,15 +332,16 @@ aside {
} }
} }
&.ropsten:before,
&.eth:before { &.eth:before {
background-image: url('../images/eth-logo.png'); background-image: url('../images/eth-logo.png');
background-size: auto 20px; background-size: auto 20px;
} }
&.rinkeby:before,
&.etc:before { &.etc:before {
background-image: url('../images/etc-logo.png'); background-image: url('../images/etc-logo.png');
background-size: auto 20px; background-size: auto 20px;
} }
&.btc:before { &.btc:before {
background-image: url('../images/btc-logo.png'); background-image: url('../images/btc-logo.png');
} }
@ -330,8 +360,6 @@ aside {
&.zec:before { &.zec:before {
background-image: url('../images/zec-logo.png'); background-image: url('../images/zec-logo.png');
} }
} }
.coin-divider { .coin-divider {
@ -387,7 +415,7 @@ aside {
.add-address { .add-address {
position: relative; position: relative;
padding: 4px 0 4px 20px; padding: 8px 0 8px 20px;
cursor: pointer; cursor: pointer;
color: @color_text_secondary; color: @color_text_secondary;
display: flex; display: flex;
@ -425,11 +453,12 @@ aside {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
font-size: 14px; font-size: 14px;
padding: 16px 0 16px 30px; padding: 8px 0 8px 22px;
white-space: nowrap; white-space: nowrap;
border-top: 1px solid @color_divider; color: @color_text_secondary;
//border-top: 1px solid @color_divider;
.loader-circle { .loader-circle {
margin-right: 12px; margin-right: 14px;
} }
} }

View File

@ -19,7 +19,7 @@ html, body {
position: relative; position: relative;
background-color: @color_body; background-color: @color_body;
font-family: @font-default; font-family: @font-default;
font-weight: 300; font-weight: 400;
font-size: 14px; font-size: 14px;
} }

View File

@ -1,17 +1,15 @@
@color_white: #ffffff; @color_white: #ffffff;
@color_header: #212121; @color_header: #212121;
@color_body: #EBEBEB; @color_body: #E3E3E3;
@color_main: #FBFBFB; @color_main: #FBFBFB;
@color_landing: #F9F9F9; @color_landing: #F9F9F9;
/// new!!! @color_text_primary: #494949;
@color_text_secondary: #757575;
@color_text_primary: #505050;
@color_text_secondary: #A9A9A9;
@color_gray_light: #F2F2F2; // hover menu @color_gray_light: #F2F2F2; // hover menu
@color_divider: #EBEBEB; @color_divider: #E3E3E3; //#EBEBEB;
@color_green_primary: #01B757; @color_green_primary: #01B757;
@color_green_secondary: #00AB51; @color_green_secondary: #00AB51;

View File

@ -12,11 +12,12 @@ article {
position: relative; position: relative;
.account-tabs { .account-tabs {
position: relative;
display: flex; display: flex;
flex: 1; flex: 1;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 0px 48px; padding: 0px 28px; // 20px padding arround links
max-width: 600px; max-width: 600px;
a { a {
@ -24,6 +25,9 @@ article {
font-size: 14px; font-size: 14px;
color: @color_text_secondary; color: @color_text_secondary;
margin: 0px 4px; margin: 0px 4px;
padding: 20px;
.hover();
&.active, &.active,
&:hover { &:hover {
color: @color_text_primary; color: @color_text_primary;
@ -37,6 +41,16 @@ article {
margin-right: 0px; margin-right: 0px;
} }
} }
.indicator {
position: absolute;
bottom: -1px;
left: 0;
width: 100px;
height: 2px;
background: @color_green_primary;
transition: all 0.3s ease-in-out;
}
} }
} }
@ -48,7 +62,7 @@ article {
h2 { h2 {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 600;
padding: 24px 48px; padding: 24px 48px;
} }

View File

@ -19,15 +19,6 @@
url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.svg#RobotoMono') format('svg'); /* Legacy iOS */ url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.svg#RobotoMono') format('svg'); /* Legacy iOS */
} }
@font-face {
font-family: 'glyphicons';
src: url('../fonts/glyphicons.eot') format('embedded-opentype'),
url('../fonts/glyphicons.eot?#iefix') format('embedded-opentype'),
url('../fonts/glyphicons.woff') format('woff'),
url('../fonts/glyphicons.ttf') format('truetype'),
url('../fonts/glyphicons.svg#icomoon') format('svg');
}
@font-face { @font-face {
font-family: 'icomoon'; font-family: 'icomoon';
src: url('../fonts/icomoon.eot') format('embedded-opentype'), src: url('../fonts/icomoon.eot') format('embedded-opentype'),
@ -37,11 +28,6 @@
url('../fonts/icomoon.svg#icomoon') format('svg'); url('../fonts/icomoon.svg#icomoon') format('svg');
} }
@font-face {
font-family: 'fontello';
src: url('../fonts/pass.ttf') format('truetype');
}
@font-default: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif; @font-default: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif;
@font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif; @font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif;
@font-family-monospace: "Roboto Mono", Menlo, Monaco, Consolas, "Courier New", monospace; @font-family-monospace: "Roboto Mono", Menlo, Monaco, Consolas, "Courier New", monospace;
@ -52,74 +38,6 @@
// background: @color_info_secondary; // background: @color_info_secondary;
// } // }
.glyphicon-base() {
display: inline-block;
font-family: 'glyphicons';
font-style: normal;
font-weight: normal;
line-height: 1;
}
.glyphicon-trezor {
.glyphicon-base();
content: "\5f";
padding-top: 1px;
font-size: 14px;
}
.glyphicon-info {
.glyphicon-base();
content: "\ea0c";
}
.glyphicon-warning {
.glyphicon-base();
content: "\ea07";
}
.glyphicon-cross {
.glyphicon-base();
content: "\ea0f";
}
.glyphicon-checkmark {
.glyphicon-base();
content: "\ea10";
}
.glyphicon-up {
.glyphicon-base();
content: "\e113";
}
.glyphicon-down {
.glyphicon-base();
content: "\e114";
}
.glyphicon-eye-open {
.glyphicon-base();
content: "\e105";
}
.glyphicon-settings {
.glyphicon-base();
content: "\e019";
}
.glyphicon-refresh {
.glyphicon-base();
content: "\e031";
}
.glyphicon-plus {
.glyphicon-base();
content: "\2b";
position: relative;
top: 1px;
}
.icomoon-base() { .icomoon-base() {
display: inline-block; display: inline-block;
font-family: 'icomoon'; font-family: 'icomoon';
@ -140,16 +58,16 @@
content: "\e902"; content: "\e902";
} }
.icomoon-info {
.icomoon-base();
content: "\e904";
}
.icomoon-refresh { .icomoon-refresh {
.icomoon-base(); .icomoon-base();
content: "\e903"; content: "\e903";
} }
.icomoon-info {
.icomoon-base();
content: "\e904";
}
.icomoon-chat { .icomoon-chat {
.icomoon-base(); .icomoon-base();
content: "\e905"; content: "\e905";
@ -175,37 +93,27 @@
content: "\e909"; content: "\e909";
} }
.icomoon-eye-error {
.icomoon-base();
content: "\e912";
}
.icomoon-T1 {
.icomoon-base();
content: "\e913";
}
.icomoon-close { .icomoon-close {
.icomoon-base(); .icomoon-base();
content: "\e90a"; content: "\e90a";
} }
.icomoon-arrow-left { .icomoon-arrow-up {
.icomoon-base(); .icomoon-base();
content: "\e90b"; content: "\e90b";
} }
.icomoon-arrow-up { .icomoon-arrow-right2 {
.icomoon-base(); .icomoon-base();
content: "\e90c"; content: "\e90c";
} }
.icomoon-arrow-right { .icomoon-plus {
.icomoon-base(); .icomoon-base();
content: "\e90d"; content: "\e90d";
} }
.icomoon-plus { .icomoon-arrow-right {
.icomoon-base(); .icomoon-base();
content: "\e90e"; content: "\e90e";
} }
@ -216,6 +124,24 @@
} }
.icomoon-eye-error {
.icomoon-base();
content: "\e911";
}
.icomoon-T1 {
.icomoon-base();
content: "\e912";
}
.icomoon-arrow-left {
.icomoon-base();
content: "\e91a";
}
.icomoon-setmax { .icomoon-setmax {
.icomoon-base(); .icomoon-base();
content: "\e91b"; content: "\e91b";
@ -235,3 +161,8 @@
.icomoon-base(); .icomoon-base();
content: "\e91e"; content: "\e91e";
} }
.icomoon-back {
.icomoon-base();
content: "\e91f";
}

View File

@ -7,21 +7,31 @@ header {
fill: @color_white; fill: @color_white;
height: 28px; height: 28px;
width: 100px; width: 100px;
margin-top: 9px; flex: 1;
display: inline-block;
}
span {
display: inline-block;
vertical-align: top;
margin-top: 16px;
margin-left: 20px;
} }
.layout-wrapper { .layout-wrapper {
width: 100%; width: 100%;
height: 100%;
max-width: 1170px; max-width: 1170px;
margin: 0 auto; margin: 0 auto;
padding: 0 15px; padding: 0 32px;
display: flex;
align-items: center;
}
a, a:visited {
color: @color_white;
margin-left: 24px;
.hover();
&:first-child {
margin: 0px;
}
&:hover,
&:active {
color: @color_text_secondary;
}
} }
} }

View File

@ -26,3 +26,4 @@
@import './inputs.less'; @import './inputs.less';
@import './loader.less'; @import './loader.less';
@import './log.less';

View File

@ -32,7 +32,7 @@ input {
&.warning { &.warning {
border-color: @color_warning_primary; border-color: @color_warning_primary;
&:focus { &:focus {
box-shadow: 0 1px 4px 0 rgba(1, 183, 87, 0.25); box-shadow: 0 1px 4px 0 rgba(235, 138, 0, 0.25);
} }
} }
@ -221,6 +221,4 @@ a.green:visited {
border-color: @color_white; border-color: @color_white;
} }
} }
} }

View File

@ -9,6 +9,7 @@
text-align: center; text-align: center;
padding-top: 65px; padding-top: 65px;
margin-top: 0px; margin-top: 0px;
background: @color_landing;
h2.claim { h2.claim {
font-size: 36px; font-size: 36px;
@ -67,6 +68,7 @@
} }
.image { .image {
position: relative;
width: 100%; width: 100%;
height: calc(100vh - 143px); height: calc(100vh - 143px);
min-height: 500px; min-height: 500px;
@ -75,6 +77,13 @@
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center 0px; background-position: center 0px;
background-size: contain; background-size: contain;
p {
position: absolute;
bottom: 32px;
left: 0;
right: 0;
margin: 0px auto;
}
} }
img { img {

50
src/styles/log.less Normal file
View File

@ -0,0 +1,50 @@
.log {
position: relative;
color: @color_info_primary;
background: @color_info_secondary;
padding: 24px 48px;
display: flex;
flex-direction: column;
text-align: left;
.log-close {
position: absolute;
top: 8px;
right: 0;
padding: 12px;
color: inherit;
transition: opacity 0.3s;
&:after {
.icomoon-close;
}
&:active,
&:hover {
opacity: 0.6;
color: inherit;
}
}
h2 {
font-size: 14px;
font-weight: bold;
padding: 0px;
}
p {
padding: 0px;
margin: 2px 0px;
font-size: 12px;
color: inherit;
}
textarea {
width: 100%;
height: 200px;
min-height: 200px;
resize: vertical;
&:focus {
box-shadow: none;
}
}
}

View File

@ -19,7 +19,7 @@
border-radius: 4px; border-radius: 4px;
background-color: @color_white; background-color: @color_white;
text-align: center; text-align: center;
overflow: hidden; // overflow: hidden;
} }
h3 { h3 {
@ -157,6 +157,8 @@
height: 80px; height: 80px;
margin-top: 15px; margin-top: 15px;
margin-left: 10px; margin-left: 10px;
font-size: 22px;
font-weight: 600;
color: @color_text_primary; color: @color_text_primary;
border: 1px solid @color_divider; border: 1px solid @color_divider;
background: @color_white; background: @color_white;
@ -186,10 +188,10 @@
input { input {
letter-spacing: 6px; letter-spacing: 6px;
line-height: 48px; line-height: 48px;
font-weight: bold; font-weight: 600;
font-size: 18px; font-size: 32px;
height: auto; height: auto;
padding: 0px 34px; padding: 0px 31px;
color: @color_text_primary; color: @color_text_primary;
background: transparent; background: transparent;
} }
@ -202,7 +204,7 @@
margin: auto 0; margin: auto 0;
padding: 0; padding: 0;
&:after { &:after {
.icomoon-arrow-left; .icomoon-back;
} }
} }

View File

@ -52,7 +52,7 @@
p { p {
padding: 0px; padding: 0px;
margin-bottom: 8px 0px; margin: 8px 0px;
color: inherit; color: inherit;
} }

View File

@ -6,6 +6,11 @@
} }
} }
.aside-tooltip-wrapper {
width: 260px;
font-size: 10px;
}
.rc-tooltip { .rc-tooltip {
position: absolute; position: absolute;
z-index: 1070; z-index: 1070;

View File

@ -1,6 +1,6 @@
// https://github.com/JedWatson/react-select/blob/master/less/select.less // https://github.com/JedWatson/react-select/blob/master/less/select.less
@import '~react-select/less/select'; @import '~react-select/less/select.less';
// override predefined colors // override predefined colors
@select-primary-color: @color_white; @select-primary-color: @color_white;
@ -10,6 +10,8 @@
@select-item-border-radius: 0px; @select-item-border-radius: 0px;
@select-input-border-color: transparent; @select-input-border-color: transparent;
@select-input-border-focus: @color_divider; @select-input-border-focus: @color_divider;
// @select-input-bg: transparent;
// @select-input-bg-focus: transparent;
.Select-focus-state(@color) { .Select-focus-state(@color) {
// do nothing // do nothing
@ -67,7 +69,13 @@
} }
.Select-noresults { .Select-noresults {
color: @color_text_secondary;
font-family: @font-default;
}
.Select-placeholder {
color: @color_text_secondary;
font-family: @font-default;
} }
.Select-value-label { .Select-value-label {
@ -90,240 +98,3 @@
} }
} }
} }
/*@select-input-height: 34px;
@select-primary-color: #fff;
@select-input-bg-focus: #ff0000;
@select-input-border-radius: 0px;
@select-input-border-focus: @select-input-border-color;
.Select {
width: 240px;
height: 34px;
display: inline-block;
vertical-align: middle;
&.is-focused:not(.is-open) > .Select-control {
border-color: @select-input-border-color;
box-shadow: none;
}
}
.Select-control {
&:hover {
box-shadow: none;
}
}
.Select-menu-outer {
border: 1px solid rgba(0, 0, 0, 0.15);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
}
.Select-option {
&:last-child {
border-radius: 0px;
}
.fee-label {
display: inline-block;
width: 70%;
}
.fee-size {
display: inline-block;
text-align: right;
}
}
.CurrencySelect {
width: 70px;
vertical-align: top;
}
.CoinSelect {
width: 290px;
height: 64px;
.Select-control {
height: 63px;
border: 0px;
border-radius: 4px 0px 0px 0px;
border-right: 1px solid @color_divider;
cursor: pointer;
transition: all 0.2s ease-in-out;
.Select-input {
background: transparent;
//display: none !important;
}
&:hover {
background: #F2F2F2;
.Select-arrow {
&:after {
color: #494949;
}
}
}
}
.Select-value {
padding: 0px;
.Select-value-label {
display: inline-block;
height: 63px;
padding-top: 20px;
padding-left: 50px;
font-size: 1.15em;
font-weight: bold;
line-height: 26px;
color: #494949;
}
&:before {
content: '';
position: absolute;
display: block;
width: 20px;
height: 20px;
z-index: 2;
left: 20px;
top: 21px;
background-image: url(../images/eth-logo.png);
background-repeat: no-repeat;
background-position: center;
background-size: auto 20px;
}
}
.Select-menu-outer {
position: relative;
top: 0;
border: 0px;
border-top: 1px solid rgba(218, 218, 218, 0.5);
border-right: 1px solid rgba(218, 218, 218, 0.5);
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.04);
max-height: none;
}
.Select-menu {
max-height: none;
overflow-x: none;
}
.Select-option {
width: 290px;
height: 64px;
padding-top: 20px;
padding-left: 60px;
position: relative;
transition: all 0.2s ease-in-out;
span {
height: 63px;
font-size: 1.15em;
font-weight: bold;
line-height: 26px;
color: #494949;
}
&:before {
content: '';
position: absolute;
display: block;
width: 20px;
height: 20px;
z-index: 2;
left: 20px;
top: 21px;
background-repeat: no-repeat;
background-position: center;
background-size: 20px 20px;
}
&.btc:before {
background-image: url('../images/btc-logo.png');
}
&.ltc:before {
background-image: url('../images/ltc-logo.png');
}
&.btg:before {
background-image: url('../images/btg-logo.png');
}
&.bch:before {
background-image: url('../images/bch-logo.png');
}
&.dash:before {
background-image: url('../images/dash-logo.png');
}
&.zec:before {
background-image: url('../images/zec-logo.png');
}
&.eth:before {
background-image: url('../images/eth-logo.png');
background-size: auto 20px;
}
&.etc:before {
background-image: url('../images/etc-logo.png');
background-size: auto 20px;
}
&:hover {
background: #F2F2F2;
}
&.is-selected {
background: yellow;
}
}
.Select-arrow-zone {
width: 28px;
}
.Select-arrow {
border: 0px;
width: 28px;
&:after {
.glyphicon-down;
color: #B3B3B3;
position: absolute;
left: 0px;
top: -8px;
transition: all 0.2s ease-in-out;
}
}
&.is-open {
.Select-arrow {
top: 0px;
&:after {
.glyphicon-up;
}
}
}
}
// /*
//
//
//
// */

View File

@ -1,5 +1,4 @@
.send-form { .send-form {
padding-bottom: 24px;
.Select { .Select {
width: 98px; width: 98px;
@ -27,6 +26,15 @@
} }
} }
&.is-disabled {
.Select-control {
cursor: default;
}
.Select-arrow {
visibility: hidden;
}
}
.fee-option { .fee-option {
display: flex; display: flex;
align-items: center; align-items: center;
@ -53,7 +61,7 @@
padding-bottom: 24px; padding-bottom: 24px;
.error, .error,
.warning, .warning:not(input),
.info { .info {
position: absolute; position: absolute;
left: 48px; left: 48px;
@ -65,7 +73,7 @@
.error { .error {
color: @color_error_primary; color: @color_error_primary;
} }
.warning { .warning:not(input) {
color: @color_warning_primary; color: @color_warning_primary;
} }
.info { .info {
@ -184,6 +192,8 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding: 0px 48px; padding: 0px 48px;
padding-bottom: 24px;
button { button {
width: 50%; width: 50%;
} }
@ -283,4 +293,58 @@
resize: none; resize: none;
height: 80px; height: 80px;
} }
.pending-transactions {
border-top: 1px solid @color_divider;
.tx {
border-bottom: 1px solid @color_divider;
padding: 14px 48px;
display: flex;
flex-direction: row;
align-items: center;
&:last-child {
border-bottom: 0px;
}
.icon {
width: 36px;
height: 36px;
//border: 8px solid white;
border-radius: 50%;
margin-right: 10px;
line-height: 30px;
text-transform: uppercase;
user-select: none;
text-align: center;
padding: 6px;
p {
line-height: 24px;
padding: 0px;
color: inherit;
}
}
.name {
flex: 1;
a, a:visited {
color: @color_text_secondary;
.hover();
&:hover, &:active {
color: @color_text_primary;
}
}
}
.balance {
color: @color_text_primary;
}
}
}
} }

View File

@ -1,8 +1,27 @@
.summary { .summary {
h2 { .summary-header {
//padding: 35px 50px 0px 50px; display: flex;
color: red; align-items: center;
&:before {
content: '';
display: inline-block;
width: 32px;
height: 22px;
background-repeat: no-repeat;
background-size: 20px 20px;
}
&.ropsten:before,
&.eth:before {
background-image: url('../images/eth-logo.png');
background-size: auto 20px;
}
&.rinkeby:before,
&.etc:before {
background-image: url('../images/etc-logo.png');
background-size: auto 20px;
}
} }
.token-select { .token-select {
@ -36,25 +55,26 @@
.summary-details { .summary-details {
position: relative; position: relative;
padding: 35px 50px 0px 50px; padding: 0px 48px;
border-bottom: 1px solid @color_divider; border-bottom: 1px solid @color_divider;
.content { .content {
display: flex;
padding-bottom: 32px;
.column { .column {
display: inline-block; margin-right: 48px;
width: 25%;
padding-bottom: 30px;
.label { .label, .value {
color: #A9A9A9; color: @color_text_secondary;
font-weight: 600; font-size: 12px;
} }
.fiat-value { .fiat-value {
font-weight: bold; font-weight: 500;
font-size: 1.2em; font-size: 18px;
margin: 7px 0 6px 0; margin: 7px 0 6px 0;
color: #494949; color: @color_text_primary;
} }
} }
} }
@ -69,45 +89,38 @@
height: 40px; height: 40px;
//line-height: 30px; //line-height: 30px;
background: @color_white; background: @color_white;
color: #B3B3B3; color: @color_text_secondary;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.04); box-shadow: 0 3px 8px rgba(0, 0, 0, 0.04);
border-radius: 50%; border-radius: 50%;
cursor: pointer; cursor: pointer;
transition: all 0.2s ease-in-out; transition: all 0.3s;
&:hover { &:hover {
background: #F2F2F2; background: @color_divider;
&:before { color: @color_text_primary;
color: #494949;
}
} }
&:before { &:before {
.glyphicon-up; .icomoon-arrow-down;
color: #B3B3B3; transition: transform 0.3s;
transform-origin: 50% 50%;
position: absolute; position: absolute;
left: 14px; left: 8px;
top: 16px; top: 10px;
transition: all 0.2s ease-in-out;
} }
} }
&.closed { &.opened {
.content { .toggle:before {
display: none; transform: rotate(180deg);
} top: 8px;
.toggle {
&:before {
.glyphicon-down;
top: 18px;
}
} }
} }
} }
.filter { .filter {
background: @color_main; //background: @color_main;
padding: 30px 48px 10px 48px; padding: 0px 48px 32px 48px;
// text-align: right; // text-align: right;
// input { // input {
@ -115,78 +128,30 @@
// } // }
} }
.add-token-form { .token-select {
position: relative; .Select-control {
.toggle { cursor: text;
cursor: pointer;
padding: 15px 50px;
} }
.content { .Select-option {
display: flex; .hover();
flex-direction: row; &.is-focused {
padding: 15px 50px; background: @color_gray_light;
}
.column {
padding-right: 10px;
label {
display: block;
color: #A9A9A9;
font-weight: 600;
} }
input { &.is-selected {
&.token-address { background: @color_divider;
width: 230px;
}
&.token-name {
width: 160px;
}
&.token-shortcut {
width: 80px;
}
&.token-decimal {
width: 80px;
}
}
button {
}
}
&:after {
.glyphicon-up;
color: #B3B3B3;
position: absolute;
right: 50px;
top: 21px;
transition: all 0.2s ease-in-out;
}
&:hover {
&:after {
color: #494949;
}
}
&.closed {
&:after {
.glyphicon-down;
} }
} }
} }
.token { .token {
border-top: 1px solid @color_divider; border-top: 1px solid @color_divider;
padding: 15px 50px; padding: 14px 48px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center;
position: relative;
.icon { .icon {
width: 36px; width: 36px;
@ -208,24 +173,27 @@
.name { .name {
flex: 1; flex: 1;
line-height: 30px; color: @color_text_secondary;
} }
.balance { button {
color: red; position: absolute;
line-height: 30px; right: 12px;
padding: 0px;
padding-top: 3px;
&:after {
.icomoon-close;
}
} }
&:last-child { &:last-child {
// border-bottom: 1px solid @color_divider; // border-bottom: 1px solid @color_divider;
} }
} }
.token-select {
.Select-control {
cursor: text;
}
}
} }

View File

@ -1,4 +1,4 @@
import { TREZOR_CONNECT_FILES, SRC, PORT } from './constants'; import { TREZOR_CONNECT_FILES, TREZOR_CONNECT_HTML, SRC, PORT } from './constants';
import path from 'path'; import path from 'path';
import webpack from 'webpack'; import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin'; import HtmlWebpackPlugin from 'html-webpack-plugin';
@ -88,10 +88,10 @@ module.exports = {
inject: true inject: true
}), }),
new CopyWebpackPlugin([ new CopyWebpackPlugin([
{ from: `${TREZOR_CONNECT_FILES}coins.json` }, { from: `${TREZOR_CONNECT_FILES}coins.json`, to: './data/coins.json' },
{ from: `${TREZOR_CONNECT_FILES}releases.json` }, { from: `${TREZOR_CONNECT_FILES}releases-1.json`, to: './data/releases-1.json' },
{ from: `${TREZOR_CONNECT_FILES}latest.txt` }, { from: `${TREZOR_CONNECT_FILES}latest.txt`, to: './data/latest.txt' },
{ from: `${TREZOR_CONNECT_FILES}config_signed.bin` }, { from: `${TREZOR_CONNECT_FILES}config_signed.bin`, to: './data/config_signed.bin' },
// { from: `${SRC}images/favicon.png` }, // { from: `${SRC}images/favicon.png` },
// { from: `${SRC}images` }, // { from: `${SRC}images` },
]), ]),

View File

@ -1,4 +1,4 @@
import { SRC, BUILD, TREZOR_LIBRARY, TREZOR_CONNECT_FILES } from './constants'; import { SRC, BUILD, TREZOR_LIBRARY, TREZOR_CONNECT_FILES, TREZOR_CONNECT_HTML } from './constants';
import webpack from 'webpack'; import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin'; import HtmlWebpackPlugin from 'html-webpack-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin'; import ExtractTextPlugin from 'extract-text-webpack-plugin';
@ -12,7 +12,7 @@ const extractLess = new ExtractTextPlugin({
module.exports = { module.exports = {
entry: { entry: {
index: ['whatwg-fetch', `${SRC}js/index.js`], index: ['whatwg-fetch', `${SRC}js/index.js`],
'trezor-library': `${TREZOR_LIBRARY}.js` //'trezor-library': `${TREZOR_LIBRARY}.js`
}, },
output: { output: {
filename: 'js/[name].[hash].js', filename: 'js/[name].[hash].js',
@ -47,17 +47,26 @@ module.exports = {
}, },
{ {
test: /\.(png|gif|jpg)$/, test: /\.(png|gif|jpg)$/,
loader: 'file-loader?name=../images/[name].[ext]' loader: 'file-loader',
query: {
publicPath: '../',
name: 'images/[name].[hash].[ext]',
}
}, },
{ {
test: /\.(ttf|eot|svg|woff|woff2)$/, test: /\.(ttf|eot|svg|woff|woff2)$/,
loader: 'file-loader?publicPath=../&name=fonts/[name].[ext]', // loader: 'file-loader?publicPath=../&name=fonts/[name].[hash].[ext]',
loader: 'file-loader',
query: {
publicPath: '../',
name: 'fonts/[name].[hash].[ext]',
}
}, },
{ {
test: /\.(wasm)$/, test: /\.(wasm)$/,
loader: 'file-loader', loader: 'file-loader',
query: { query: {
name: 'js/[name].[ext]', name: 'js/[name].[hash].[ext]',
}, },
}, },
{ {
@ -88,8 +97,11 @@ module.exports = {
//{from: `${SRC}/app/robots.txt`}, //{from: `${SRC}/app/robots.txt`},
//{ from: `${SRC}js/vendor`, to: `${BUILD}js/vendor` }, //{ from: `${SRC}js/vendor`, to: `${BUILD}js/vendor` },
//{ from: `${SRC}config.json` }, //{ from: `${SRC}config.json` },
{ from: `${SRC}images`, to: `${BUILD}images` }, //{ from: `${SRC}images/favicon.png`, to: `${BUILD}favicon.png` },
{ from: `${SRC}data`, to: `${BUILD}data` }, { from: `${SRC}images/favicon.ico`, to: `${BUILD}favicon.ico` },
{ from: `${SRC}images/favicon.png`, to: `${BUILD}favicon.png` },
{ from: `${SRC}images/dashboard.png`, to: `${BUILD}images/dashboard.png` },
{ from: `${SRC}data`, to: `${BUILD}data`, cache: false },
]), ]),
new webpack.optimize.OccurrenceOrderPlugin(), new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.NoEmitOnErrorsPlugin(), new webpack.NoEmitOnErrorsPlugin(),
@ -99,10 +111,11 @@ module.exports = {
// } // }
// }), // }),
new CopyWebpackPlugin([ new CopyWebpackPlugin([
{ from: `${TREZOR_CONNECT_FILES}coins.json` }, { from: `${TREZOR_CONNECT_FILES}coins.json`, to: `${BUILD}/data/coins.json` },
{ from: `${TREZOR_CONNECT_FILES}releases.json` }, { from: `${TREZOR_CONNECT_FILES}releases-1.json`, to: `${BUILD}/data/releases-1.json` },
{ from: `${TREZOR_CONNECT_FILES}latest.txt` }, { from: `${TREZOR_CONNECT_FILES}releases-2.json`, to: `${BUILD}/data/releases-2.json` },
{ from: `${TREZOR_CONNECT_FILES}config_signed.bin` }, { from: `${TREZOR_CONNECT_FILES}latest.txt`, to: `${BUILD}/data/latest.txt` },
{ from: `${TREZOR_CONNECT_FILES}config_signed.bin`, to: `${BUILD}/data/config_signed.bin` },
]), ]),
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'), 'process.env.NODE_ENV': JSON.stringify('production'),

607
yarn.lock

File diff suppressed because it is too large Load Diff