aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.circleci/config.yml58
-rw-r--r--.gitignore1
-rw-r--r--CHANGELOG.md11
-rw-r--r--app/_locales/en/messages.json884
-rw-r--r--app/_locales/es/messages.json886
-rw-r--r--app/_locales/fr/messages.json609
-rw-r--r--app/_locales/it/messages.json819
-rw-r--r--app/_locales/ja/messages.json605
-rw-r--r--app/_locales/ko/messages.json609
-rw-r--r--app/_locales/ph/messages.json609
-rw-r--r--app/_locales/pt/messages.json819
-rw-r--r--app/_locales/vi/messages.json609
-rw-r--r--app/_locales/zh_CN/messages.json605
-rw-r--r--app/images/arrow-right.svg16
-rw-r--r--app/images/deposit-eth.svg32
-rw-r--r--app/manifest.json7
-rw-r--r--app/scripts/background.js35
-rw-r--r--app/scripts/contentscript.js17
-rw-r--r--app/scripts/controllers/transactions.js53
-rw-r--r--app/scripts/lib/local-store.js62
-rw-r--r--app/scripts/lib/tx-gas-utils.js6
-rw-r--r--app/scripts/lib/tx-state-manager.js42
-rw-r--r--development/genStates.js31
-rw-r--r--development/mock-dev.js (renamed from mock-dev.js)22
-rw-r--r--development/run-version-bump.js2
-rw-r--r--development/states/confirm-new-ui.json2
-rw-r--r--development/states/send-edit.json2
-rw-r--r--development/ui-dev.js (renamed from ui-dev.js)10
-rw-r--r--development/verify-locale-strings.js96
-rw-r--r--docs/add-to-chrome.md18
-rw-r--r--docs/translating-guide.md26
-rw-r--r--gulpfile.js6
-rw-r--r--mascara/src/app/first-time/backup-phrase-screen.js52
-rw-r--r--mascara/src/app/first-time/create-password-screen.js17
-rw-r--r--mascara/src/app/first-time/import-seed-phrase-screen.js129
-rw-r--r--mascara/src/app/first-time/index.css201
-rw-r--r--mascara/src/app/first-time/notice-screen.js46
-rw-r--r--mascara/src/app/first-time/unique-image-screen.js34
-rw-r--r--old-ui/app/components/transaction-list-item.js18
-rw-r--r--old-ui/app/components/transaction-list.js2
-rw-r--r--old-ui/app/css/index.css4
-rw-r--r--old-ui/app/keychains/hd/restore-vault.js13
-rw-r--r--package-lock.json20
-rw-r--r--package.json20
-rw-r--r--test/base.conf.js4
-rw-r--r--test/integration/index.js2
-rw-r--r--test/integration/lib/add-token.js73
-rw-r--r--test/integration/lib/confirm-sig-requests.js44
-rw-r--r--test/integration/lib/first-time.js80
-rw-r--r--test/integration/lib/mascara-first-time.js94
-rw-r--r--test/integration/lib/send-new-ui.js137
-rw-r--r--test/lib/react-trigger-change.js161
-rw-r--r--test/lib/util.js53
-rw-r--r--test/unit/components/binary-renderer-test.js2
-rw-r--r--test/unit/components/bn-as-decimal-input-test.js2
-rw-r--r--test/unit/development/version–bump-test.js4
-rw-r--r--test/unit/nameForAccount_test.js2
-rw-r--r--test/unit/tx-controller-test.js45
-rw-r--r--ui/app/account-detail.js117
-rw-r--r--ui/app/accounts/import/index.js15
-rw-r--r--ui/app/accounts/import/json.js17
-rw-r--r--ui/app/accounts/import/private-key.js13
-rw-r--r--ui/app/accounts/import/seed.js6
-rw-r--r--ui/app/accounts/new-account/create-form.js13
-rw-r--r--ui/app/accounts/new-account/index.js9
-rw-r--r--ui/app/actions.js14
-rw-r--r--ui/app/add-token.js43
-rw-r--r--ui/app/app.js63
-rw-r--r--ui/app/components/account-dropdowns.js15
-rw-r--r--ui/app/components/account-export.js15
-rw-r--r--ui/app/components/account-menu/index.js17
-rw-r--r--ui/app/components/balance.js89
-rw-r--r--ui/app/components/binary-renderer.js46
-rw-r--r--ui/app/components/bn-as-decimal-input.js9
-rw-r--r--ui/app/components/buy-button-subview.js15
-rw-r--r--ui/app/components/coinbase-form.js5
-rw-r--r--ui/app/components/copyButton.js3
-rw-r--r--ui/app/components/copyable.js3
-rw-r--r--ui/app/components/currency-input.js22
-rw-r--r--ui/app/components/customize-gas-modal/index.js56
-rw-r--r--ui/app/components/dropdowns/account-options-dropdown.js29
-rw-r--r--ui/app/components/dropdowns/account-selection-dropdown.js29
-rw-r--r--ui/app/components/dropdowns/components/account-dropdowns.js22
-rw-r--r--ui/app/components/dropdowns/index.js6
-rw-r--r--ui/app/components/dropdowns/network-dropdown.js27
-rw-r--r--ui/app/components/dropdowns/token-menu-dropdown.js3
-rw-r--r--ui/app/components/ens-input.js5
-rw-r--r--ui/app/components/hex-as-decimal-input.js9
-rw-r--r--ui/app/components/input-number.js1
-rw-r--r--ui/app/components/mini-account-panel.js74
-rw-r--r--ui/app/components/modals/account-details-modal.js5
-rw-r--r--ui/app/components/modals/account-modal-container.js3
-rw-r--r--ui/app/components/modals/buy-options-modal.js15
-rw-r--r--ui/app/components/modals/deposit-ether-modal.js131
-rw-r--r--ui/app/components/modals/edit-account-name-modal.js7
-rw-r--r--ui/app/components/modals/export-private-key-modal.js16
-rw-r--r--ui/app/components/modals/hide-token-confirmation-modal.js13
-rw-r--r--ui/app/components/modals/modal.js21
-rw-r--r--ui/app/components/modals/new-account-modal.js17
-rw-r--r--ui/app/components/network.js25
-rw-r--r--ui/app/components/notice.js3
-rw-r--r--ui/app/components/pending-msg-details.js4
-rw-r--r--ui/app/components/pending-msg.js15
-rw-r--r--ui/app/components/pending-personal-msg-details.js60
-rw-r--r--ui/app/components/pending-tx/confirm-deploy-contract.js590
-rw-r--r--ui/app/components/pending-tx/confirm-send-ether.js409
-rw-r--r--ui/app/components/pending-tx/confirm-send-token.js244
-rw-r--r--ui/app/components/pending-typed-msg-details.js59
-rw-r--r--ui/app/components/pending-typed-msg.js46
-rw-r--r--ui/app/components/range-slider.js58
-rw-r--r--ui/app/components/send-token/index.js439
-rw-r--r--ui/app/components/send/currency-toggle.js44
-rw-r--r--ui/app/components/send/eth-fee-display.js37
-rw-r--r--ui/app/components/send/gas-fee-display-v2.js13
-rw-r--r--ui/app/components/send/gas-fee-display.js62
-rw-r--r--ui/app/components/send/gas-tooltip.js4
-rw-r--r--ui/app/components/send/send-v2-container.js1
-rw-r--r--ui/app/components/send/to-autocomplete.js4
-rw-r--r--ui/app/components/send/usd-fee-display.js35
-rw-r--r--ui/app/components/sender-to-recipient.js45
-rw-r--r--ui/app/components/shapeshift-form.js23
-rw-r--r--ui/app/components/shift-list-item.js15
-rw-r--r--ui/app/components/signature-request.js25
-rw-r--r--ui/app/components/template.js18
-rw-r--r--ui/app/components/token-list.js7
-rw-r--r--ui/app/components/transaction-list-item-icon.js68
-rw-r--r--ui/app/components/transaction-list-item.js238
-rw-r--r--ui/app/components/transaction-list.js87
-rw-r--r--ui/app/components/tx-list-item.js100
-rw-r--r--ui/app/components/tx-list.js20
-rw-r--r--ui/app/components/tx-view.js21
-rw-r--r--ui/app/components/typed-message-renderer.js42
-rw-r--r--ui/app/components/wallet-content-display.js56
-rw-r--r--ui/app/components/wallet-view.js9
-rw-r--r--ui/app/conf-tx.js18
-rw-r--r--ui/app/conversion-util.js13
-rw-r--r--ui/app/css/itcss/components/account-menu.scss3
-rw-r--r--ui/app/css/itcss/components/buttons.scss12
-rw-r--r--ui/app/css/itcss/components/confirm.scss48
-rw-r--r--ui/app/css/itcss/components/hero-balance.scss3
-rw-r--r--ui/app/css/itcss/components/index.scss4
-rw-r--r--ui/app/css/itcss/components/modal.scss58
-rw-r--r--ui/app/css/itcss/components/network.scss3
-rw-r--r--ui/app/css/itcss/components/new-account.scss3
-rw-r--r--ui/app/css/itcss/components/newui-sections.scss24
-rw-r--r--ui/app/css/itcss/components/send.scss27
-rw-r--r--ui/app/css/itcss/components/sender-to-recipient.scss58
-rw-r--r--ui/app/css/itcss/components/transaction-list.scss55
-rw-r--r--ui/app/css/itcss/components/welcome-screen.scss59
-rw-r--r--ui/app/css/itcss/generic/index.scss54
-rw-r--r--ui/app/css/itcss/generic/reset.scss4
-rw-r--r--ui/app/css/itcss/settings/variables.scss1
-rw-r--r--ui/app/first-time/init-menu.js19
-rw-r--r--ui/app/info.js154
-rw-r--r--ui/app/keychains/hd/recover-seed/confirmation.js7
-rw-r--r--ui/app/keychains/hd/restore-vault.js42
-rw-r--r--ui/app/main-container.js14
-rw-r--r--ui/app/reducers/metamask.js8
-rw-r--r--ui/app/selectors.js5
-rw-r--r--ui/app/send-v2.js39
-rw-r--r--ui/app/send.js547
-rw-r--r--ui/app/settings.js76
-rw-r--r--ui/app/template.js30
-rw-r--r--ui/app/token-tracker.js0
-rw-r--r--ui/app/unlock.js144
-rw-r--r--ui/app/welcome-screen.js56
-rw-r--r--ui/i18n.js33
-rw-r--r--ui/lib/contract-namer.js33
-rw-r--r--yarn.lock668
169 files changed, 10465 insertions, 4689 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 9cd0a24ec..c14909783 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -15,12 +15,20 @@ workflows:
- test-unit:
requires:
- prep-deps-npm
- - test-integration-mascara:
+ - test-integration-mascara-chrome:
+ requires:
+ - prep-deps-npm
+ - prep-scss
+ - test-integration-mascara-firefox:
requires:
- prep-deps-npm
- prep-deps-firefox
- prep-scss
- - test-integration-flat:
+ - test-integration-flat-chrome:
+ requires:
+ - prep-deps-npm
+ - prep-scss
+ - test-integration-flat-firefox:
requires:
- prep-deps-npm
- prep-deps-firefox
@@ -99,7 +107,9 @@ jobs:
name: test:coverage
command: npm run test:coverage
- test-integration-flat:
+ test-integration-flat-firefox:
+ environment:
+ browsers: '["Firefox"]'
docker:
- image: circleci/node:8-browsers
steps:
@@ -125,7 +135,28 @@ jobs:
name: test:integration:flat
command: npm run test:flat
- test-integration-mascara:
+ test-integration-flat-chrome:
+ environment:
+ browsers: '["Chrome"]'
+ docker:
+ - image: circleci/node:8-browsers
+ steps:
+ - checkout
+ - restore_cache:
+ key: dependency-cache-{{ checksum "package-lock.json" }}
+ - run:
+ name: Get Scss Cache key
+ # this allows us to checksum against a whole directory
+ command: find ui/app/css -type f -exec md5sum {} \; | sort -k 2 > scss_checksum
+ - restore_cache:
+ key: scss-cache-{{ checksum "scss_checksum" }}
+ - run:
+ name: test:integration:flat
+ command: npm run test:flat
+
+ test-integration-mascara-firefox:
+ environment:
+ browsers: '["Firefox"]'
docker:
- image: circleci/node:8-browsers
steps:
@@ -150,3 +181,22 @@ jobs:
- run:
name: test:integration:mascara
command: npm run test:mascara
+
+ test-integration-mascara-chrome:
+ environment:
+ browsers: '["Chrome"]'
+ docker:
+ - image: circleci/node:8-browsers
+ steps:
+ - checkout
+ - restore_cache:
+ key: dependency-cache-{{ checksum "package-lock.json" }}
+ - run:
+ name: Get Scss Cache key
+ # this allows us to checksum against a whole directory
+ command: find ui/app/css -type f -exec md5sum {} \; | sort -k 2 > scss_checksum
+ - restore_cache:
+ key: scss-cache-{{ checksum "scss_checksum" }}
+ - run:
+ name: test:integration:mascara
+ command: npm run test:mascara
diff --git a/.gitignore b/.gitignore
index 059315545..f2a678777 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
npm-debug.log
node_modules
+yarn.lock
app/bower_components
test/bower_components
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 177e08bdd..75ba7670f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,18 @@
# Changelog
## Current Master
+
+- MetaMask will no longer allow nonces to be specified by the dapp
+- Add ability for internationalization.
+- Will now throw an error if the `to` field in txParams is not valid.
+- Will strip null values from the `to` field.
- Fix flashing to Log in screen after logging in or restoring from seed phrase.
+- Increase tap areas for menu buttons on mobile
+- Change all fonts in new-ui onboarding to Roboto, size 400
+- Add a welcome screen to new-ui onboarding flow
+- Make new-ui create password screen responsive
+- Hide network dropdown before account is initialized
+- Fix bug that could prevent MetaMask from saving the latest vault.
## 4.2.0 Tue Mar 06 2018
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 8c28f1c43..c64b7248b 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -1,10 +1,888 @@
{
+ "accept": {
+ "message": "Accept"
+ },
+ "account": {
+ "message": "Account"
+ },
+ "accountDetails": {
+ "message": "Account Details"
+ },
+ "accountName": {
+ "message": "Account Name"
+ },
+ "address": {
+ "message": "Address"
+ },
+ "addCustomToken": {
+ "message": "Add custom token"
+ },
+ "addToken": {
+ "message": "Add Token"
+ },
+ "addTokens": {
+ "message": "Add Tokens"
+ },
+ "amount": {
+ "message": "Amount"
+ },
+ "amountPlusGas": {
+ "message": "Amount + Gas"
+ },
+ "appDescription": {
+ "message": "Ethereum Browser Extension",
+ "description": "The description of the application"
+ },
"appName": {
"message": "MetaMask",
"description": "The name of the application"
},
- "appDescription": {
- "message": "Ethereum Identity Management",
- "description": "The description of the application"
+ "approved": {
+ "message": "Approved"
+ },
+ "attemptingConnect": {
+ "message": "Attempting to connect to blockchain."
+ },
+ "attributions": {
+ "message": "Attributions"
+ },
+ "available": {
+ "message": "Available"
+ },
+ "back": {
+ "message": "Back"
+ },
+ "balance": {
+ "message": "Balance:"
+ },
+ "balances": {
+ "message": "Your balances"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Insufficient balance for current gas total"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "must be greater than or equal to $1 and less than or equal to $2.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "blockiesIdenticon": {
+ "message": "Use Blockies Identicon"
+ },
+ "borrowDharma": {
+ "message": "Borrow With Dharma (Beta)"
+ },
+ "builtInCalifornia": {
+ "message": "MetaMask is designed and built in California."
+ },
+ "buy": {
+ "message": "Buy"
+ },
+ "buyCoinbase": {
+ "message": "Buy on Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase is the world’s most popular way to buy and sell bitcoin, ethereum, and litecoin."
+ },
+ "ok": {
+ "message": "Ok"
+ },
+ "cancel": {
+ "message": "Cancel"
+ },
+ "classicInterface": {
+ "message": "Use classic interface"
+ },
+ "clickCopy": {
+ "message": "Click to Copy"
+ },
+ "confirm": {
+ "message": "Confirm"
+ },
+ "confirmed": {
+ "message": "Confirmed"
+ },
+ "confirmContract": {
+ "message": "Confirm Contract"
+ },
+ "confirmPassword": {
+ "message": "Confirm Password"
+ },
+ "confirmTransaction": {
+ "message": "Confirm Transaction"
+ },
+ "continue": {
+ "message": "Continue"
+ },
+ "continueToCoinbase": {
+ "message": "Continue to Coinbase"
+ },
+ "contractDeployment": {
+ "message": "Contract Deployment"
+ },
+ "conversionProgress": {
+ "message": "Conversion in progress"
+ },
+ "copiedButton": {
+ "message": "Copied"
+ },
+ "copiedClipboard": {
+ "message": "Copied to Clipboard"
+ },
+ "copiedExclamation": {
+ "message": "Copied!"
+ },
+ "copiedSafe": {
+ "message": "I've copied it somewhere safe"
+ },
+ "copy": {
+ "message": "Copy"
+ },
+ "copyToClipboard": {
+ "message": "Copy to clipboard"
+ },
+ "copyButton": {
+ "message": " Copy "
+ },
+ "copyPrivateKey": {
+ "message": "This is your private key (click to copy)"
+ },
+ "create": {
+ "message": "Create"
+ },
+ "createAccount": {
+ "message": "Create Account"
+ },
+ "createDen": {
+ "message": "Create"
+ },
+ "crypto": {
+ "message": "Crypto",
+ "description": "Exchange type (cryptocurrencies)"
+ },
+ "currentConversion": {
+ "message": "Current Conversion"
+ },
+ "currentNetwork": {
+ "message": "Current Network"
+ },
+ "customGas": {
+ "message": "Customize Gas"
+ },
+ "customize": {
+ "message": "Customize"
+ },
+ "customRPC": {
+ "message": "Custom RPC"
+ },
+ "decimalsMustZerotoTen": {
+ "message": "Decimals must be at least 0, and not over 36."
+ },
+ "decimal": {
+ "message": "Decimals of Precision"
+ },
+ "defaultNetwork": {
+ "message": "The default network for Ether transactions is Main Net."
+ },
+ "denExplainer": {
+ "message": "Your DEN is your password-encrypted storage within MetaMask."
+ },
+ "deposit": {
+ "message": "Deposit"
+ },
+ "depositBTC": {
+ "message": "Deposit your BTC to the address below:"
+ },
+ "depositCoin": {
+ "message": "Deposit your $1 to the address below",
+ "description": "Tells the user what coin they have selected to deposit with shapeshift"
+ },
+ "depositEth": {
+ "message": "Deposit Eth"
+ },
+ "depositEther": {
+ "message": "Deposit Ether"
+ },
+ "depositFiat": {
+ "message": "Deposit with Fiat"
+ },
+ "depositFromAccount": {
+ "message": "Deposit from another account"
+ },
+ "depositShapeShift": {
+ "message": "Deposit with ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "If you own other cryptocurrencies, you can trade and deposit Ether directly into your MetaMask wallet. No Account Needed."
+ },
+ "details": {
+ "message": "Details"
+ },
+ "directDeposit": {
+ "message": "Direct Deposit"
+ },
+ "directDepositEther": {
+ "message": "Directly Deposit Ether"
+ },
+ "directDepositEtherExplainer": {
+ "message": "If you already have some Ether, the quickest way to get Ether in your new wallet by direct deposit."
+ },
+ "done": {
+ "message": "Done"
+ },
+ "downloadStatelogs": {
+ "message": "Download State Logs"
+ },
+ "dropped": {
+ "message": "Dropped"
+ },
+ "edit": {
+ "message": "Edit"
+ },
+ "editAccountName": {
+ "message": "Edit Account Name"
+ },
+ "emailUs": {
+ "message": "Email us!"
+ },
+ "encryptNewDen": {
+ "message": "Encrypt your new DEN"
+ },
+ "enterPassword": {
+ "message": "Enter password"
+ },
+ "enterPasswordConfirm": {
+ "message": "Enter your password to confirm"
+ },
+ "passwordNotLongEnough": {
+ "message": "Password not long enough"
+ },
+ "passwordsDontMatch": {
+ "message": "Passwords Don't Match"
+ },
+ "etherscanView": {
+ "message": "View account on Etherscan"
+ },
+ "exchangeRate": {
+ "message": "Exchange Rate"
+ },
+ "exportPrivateKey": {
+ "message": "Export Private Key"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "Export private keys at your own risk."
+ },
+ "failed": {
+ "message": "Failed"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Exchange type"
+ },
+ "fileImportFail": {
+ "message": "File import not working? Click here!",
+ "description": "Helps user import their account from a JSON file"
+ },
+ "followTwitter": {
+ "message": "Follow us on Twitter"
+ },
+ "from": {
+ "message": "From"
+ },
+ "fromToSame": {
+ "message": "From and To address cannot be the same"
+ },
+ "fromShapeShift": {
+ "message": "From ShapeShift"
+ },
+ "gas": {
+ "message": "Gas",
+ "description": "Short indication of gas cost"
+ },
+ "gasFee": {
+ "message": "Gas Fee"
+ },
+ "gasLimit": {
+ "message": "Gas Limit"
+ },
+ "gasLimitCalculation": {
+ "message": "We calculate the suggested gas limit based on network success rates."
+ },
+ "gasLimitRequired": {
+ "message": "Gas Limit Required"
+ },
+ "gasLimitTooLow": {
+ "message": "Gas limit must be at least 21000"
+ },
+ "generatingSeed": {
+ "message": "Generating Seed..."
+ },
+ "gasPrice": {
+ "message": "Gas Price (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "We calculate the suggested gas prices based on network success rates."
+ },
+ "gasPriceRequired": {
+ "message": "Gas Price Required"
+ },
+ "getEther": {
+ "message": "Get Ether"
+ },
+ "getEtherFromFaucet": {
+ "message": "Get Ether from a faucet for the $1",
+ "description": "Displays network name for Ether faucet"
+ },
+ "greaterThanMin": {
+ "message": "must be greater than or equal to $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "here": {
+ "message": "here",
+ "description": "as in -click here- for more information (goes with troubleTokenBalances)"
+ },
+ "hereList": {
+ "message": "Here's a list!!!!"
+ },
+ "hide": {
+ "message": "Hide"
+ },
+ "hideToken": {
+ "message": "Hide Token"
+ },
+ "hideTokenPrompt": {
+ "message": "Hide Token?"
+ },
+ "howToDeposit": {
+ "message": "How would you like to deposit Ether?"
+ },
+ "holdEther": {
+ "message": "It allows you to hold ether & tokens, and serves as your bridge to decentralized applications."
+ },
+ "import": {
+ "message": "Import",
+ "description": "Button to import an account from a selected file"
+ },
+ "importAccount": {
+ "message": "Import Account"
+ },
+ "importAccountMsg": {
+ "message":" Imported accounts will not be associated with your originally created MetaMask account seedphrase. Learn more about imported accounts "
+ },
+ "importAnAccount": {
+ "message": "Import an account"
+ },
+ "importDen": {
+ "message": "Import Existing DEN"
+ },
+ "imported": {
+ "message": "Imported",
+ "description": "status showing that an account has been fully loaded into the keyring"
+ },
+ "infoHelp": {
+ "message": "Info & Help"
+ },
+ "insufficientFunds": {
+ "message": "Insufficient funds."
+ },
+ "insufficientTokens": {
+ "message": "Insufficient tokens."
+ },
+ "invalidAddress": {
+ "message": "Invalid address"
+ },
+ "invalidAddressRecipient": {
+ "message": "Recipient address is invalid"
+ },
+ "invalidGasParams": {
+ "message": "Invalid Gas Parameters"
+ },
+ "invalidInput": {
+ "message": "Invalid input."
+ },
+ "invalidRequest": {
+ "message": "Invalid Request"
+ },
+ "invalidRPC": {
+ "message": "Invalid RPC URI"
+ },
+ "jsonFail": {
+ "message": "Something went wrong. Please make sure your JSON file is properly formatted."
+ },
+ "jsonFile": {
+ "message": "JSON File",
+ "description": "format for importing an account"
+ },
+ "kovan": {
+ "message": "Kovan Test Network"
+ },
+ "knowledgeDataBase": {
+ "message": "Visit our Knowledge Base"
+ },
+ "max": {
+ "message": "Max"
+ },
+ "lessThanMax": {
+ "message": "must be less than or equal to $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "likeToAddTokens": {
+ "message": "Would you like to add these tokens?"
+ },
+ "links": {
+ "message": "Links"
+ },
+ "limit": {
+ "message": "Limit"
+ },
+ "loading": {
+ "message": "Loading..."
+ },
+ "loadingTokens": {
+ "message": "Loading Tokens..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "login": {
+ "message": "Log In"
+ },
+ "logout": {
+ "message": "Log out"
+ },
+ "loose": {
+ "message": "Loose"
+ },
+ "loweCaseWords": {
+ "message": "seed words only have lowercase characters"
+ },
+ "mainnet": {
+ "message": "Main Ethereum Network"
+ },
+ "message": {
+ "message": "Message"
+ },
+ "metamaskDescription": {
+ "message": "MetaMask is a secure identity vault for Ethereum."
+ },
+ "min": {
+ "message": "Minimum"
+ },
+ "myAccounts": {
+ "message": "My Accounts"
+ },
+ "mustSelectOne": {
+ "message": "Must select at least 1 token."
+ },
+ "needEtherInWallet": {
+ "message": "To interact with decentralized applications using MetaMask, you’ll need Ether in your wallet."
+ },
+ "needImportFile": {
+ "message": "You must select a file to import.",
+ "description": "User is important an account and needs to add a file to continue"
+ },
+ "needImportPassword": {
+ "message": "You must enter a password for the selected file.",
+ "description": "Password and file needed to import an account"
+ },
+ "negativeETH": {
+ "message": "Can not send negative amounts of ETH."
+ },
+ "networks": {
+ "message": "Networks"
+ },
+ "newAccount": {
+ "message": "New Account"
+ },
+ "newAccountNumberName": {
+ "message": "Account $1",
+ "description": "Default name of next account to be created on create account screen"
+ },
+ "newContract": {
+ "message": "New Contract"
+ },
+ "newPassword": {
+ "message": "New Password (min 8 chars)"
+ },
+ "newRecipient": {
+ "message": "New Recipient"
+ },
+ "newRPC": {
+ "message": "New RPC URL"
+ },
+ "next": {
+ "message": "Next"
+ },
+ "noAddressForName": {
+ "message": "No address has been set for this name."
+ },
+ "noDeposits": {
+ "message": "No deposits received"
+ },
+ "noTransactionHistory": {
+ "message": "No transaction history."
+ },
+ "noTransactions": {
+ "message": "No Transactions"
+ },
+ "notStarted": {
+ "message": "Not Started"
+ },
+ "oldUI": {
+ "message": "Old UI"
+ },
+ "oldUIMessage": {
+ "message": "You have returned to the old UI. You can switch back to the New UI through the option in the top right dropdown menu."
+ },
+ "or": {
+ "message": "or",
+ "description": "choice between creating or importing a new account"
+ },
+ "passwordCorrect": {
+ "message": "Please make sure your password is correct."
+ },
+ "passwordMismatch": {
+ "message": "passwords don't match",
+ "description": "in password creation process, the two new password fields did not match"
+ },
+ "passwordShort": {
+ "message": "password not long enough",
+ "description": "in password creation process, the password is not long enough to be secure"
+ },
+ "pastePrivateKey": {
+ "message": "Paste your private key string here:",
+ "description": "For importing an account from a private key"
+ },
+ "pasteSeed": {
+ "message": "Paste your seed phrase here!"
+ },
+ "personalAddressDetected": {
+ "message": "Personal address detected. Input the token contract address."
+ },
+ "pleaseReviewTransaction": {
+ "message": "Please review your transaction."
+ },
+ "privacyMsg": {
+ "message": "Privacy Policy"
+ },
+ "privateKey": {
+ "message": "Private Key",
+ "description": "select this type of file to use to import an account"
+ },
+ "privateKeyWarning": {
+ "message": "Warning: Never disclose this key. Anyone with your private keys can take steal any assets held in your account."
+ },
+ "privateNetwork": {
+ "message": "Private Network"
+ },
+ "qrCode": {
+ "message": "Show QR Code"
+ },
+ "readdToken": {
+ "message": "You can add this token back in the future by going go to “Add token” in your accounts options menu."
+ },
+ "readMore": {
+ "message": "Read more here."
+ },
+ "readMore2": {
+ "message": "Read more."
+ },
+ "receive": {
+ "message": "Receive"
+ },
+ "recipientAddress": {
+ "message": "Recipient Address"
+ },
+ "refundAddress": {
+ "message": "Your Refund Address"
+ },
+ "rejected": {
+ "message": "Rejected"
+ },
+ "resetAccount": {
+ "message": "Reset Account"
+ },
+ "restoreFromSeed": {
+ "message": "Restore from seed phrase"
+ },
+ "restoreVault": {
+ "message": "Restore Vault"
+ },
+ "required": {
+ "message": "Required"
+ },
+ "retryWithMoreGas": {
+ "message": "Retry with a higher gas price here"
+ },
+ "walletSeed": {
+ "message": "Wallet Seed"
+ },
+ "revealSeedWords": {
+ "message": "Reveal Seed Words"
+ },
+ "revealSeedWordsWarning": {
+ "message": "Do not recover your seed words in a public place! These words can be used to steal all your accounts."
+ },
+ "revert": {
+ "message": "Revert"
+ },
+ "rinkeby": {
+ "message": "Rinkeby Test Network"
+ },
+ "ropsten": {
+ "message": "Ropsten Test Network"
+ },
+ "currentRpc": {
+ "message": "Current RPC"
+ },
+ "connectingToMainnet": {
+ "message": "Connecting to Main Ethereum Network"
+ },
+ "connectingToRopsten": {
+ "message": "Connecting to Ropsten Test Network"
+ },
+ "connectingToKovan": {
+ "message": "Connecting to Kovan Test Network"
+ },
+ "connectingToRinkeby": {
+ "message": "Connecting to Rinkeby Test Network"
+ },
+ "connectingToUnknown": {
+ "message": "Connecting to Unknown Network"
+ },
+ "sampleAccountName": {
+ "message": "E.g. My new account",
+ "description": "Help user understand concept of adding a human-readable name to their account"
+ },
+ "save": {
+ "message": "Save"
+ },
+ "saveAsFile": {
+ "message": "Save as File",
+ "description": "Account export process"
+ },
+ "saveSeedAsFile": {
+ "message": "Save Seed Words As File"
+ },
+ "search": {
+ "message": "Search"
+ },
+ "secretPhrase": {
+ "message": "Enter your secret twelve word phrase here to restore your vault."
+ },
+ "newPassword8Chars": {
+ "message": "New Password (min 8 chars)"
+ },
+ "seedPhraseReq": {
+ "message": "seed phrases are 12 words long"
+ },
+ "select": {
+ "message": "Select"
+ },
+ "selectCurrency": {
+ "message": "Select Currency"
+ },
+ "selectService": {
+ "message": "Select Service"
+ },
+ "selectType": {
+ "message": "Select Type"
+ },
+ "send": {
+ "message": "Send"
+ },
+ "sendETH": {
+ "message": "Send ETH"
+ },
+ "sendTokens": {
+ "message": "Send Tokens"
+ },
+ "onlySendToEtherAddress": {
+ "message": "Only send ETH to an Ethereum address."
+ },
+ "sendTokensAnywhere": {
+ "message": "Send Tokens to anyone with an Ethereum account"
+ },
+ "settings": {
+ "message": "Settings"
+ },
+ "info": {
+ "message": "Info"
+ },
+ "shapeshiftBuy": {
+ "message": "Buy with Shapeshift"
+ },
+ "showPrivateKeys": {
+ "message": "Show Private Keys"
+ },
+ "showQRCode": {
+ "message": "Show QR Code"
+ },
+ "sign": {
+ "message": "Sign"
+ },
+ "signed": {
+ "message": "Signed"
+ },
+ "signMessage": {
+ "message": "Sign Message"
+ },
+ "signNotice": {
+ "message": "Signing this message can have \ndangerous side effects. Only sign messages from \nsites you fully trust with your entire account.\n This dangerous method will be removed in a future version. "
+ },
+ "sigRequest": {
+ "message": "Signature Request"
+ },
+ "sigRequested": {
+ "message": "Signature Requested"
+ },
+ "spaceBetween": {
+ "message": "there can only be a space between words"
+ },
+ "status": {
+ "message": "Status"
+ },
+ "stateLogs": {
+ "message": "State Logs"
+ },
+ "stateLogsDescription": {
+ "message": "State logs contain your public account addresses and sent transactions."
+ },
+ "stateLogError": {
+ "message": "Error in retrieving state logs."
+ },
+ "submit": {
+ "message": "Submit"
+ },
+ "submitted": {
+ "message": "Submitted"
+ },
+ "supportCenter": {
+ "message": "Visit our Support Center"
+ },
+ "symbolBetweenZeroTen": {
+ "message": "Symbol must be between 0 and 10 characters."
+ },
+ "takesTooLong": {
+ "message": "Taking too long?"
+ },
+ "terms": {
+ "message": "Terms of Use"
+ },
+ "testFaucet": {
+ "message": "Test Faucet"
+ },
+ "to": {
+ "message": "To: "
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 to ETH via ShapeShift",
+ "description": "system will fill in deposit type in start of message"
+ },
+ "tokenAddress": {
+ "message": "Token Address"
+ },
+ "tokenAlreadyAdded": {
+ "message": "Token has already been added."
+ },
+ "tokenBalance": {
+ "message": "Your Token Balance is:"
+ },
+ "tokenSelection": {
+ "message": "Search for tokens or select from our list of popular tokens."
+ },
+ "tokenSymbol": {
+ "message": "Token Symbol"
+ },
+ "tokenWarning1": {
+ "message": "Keep track of the tokens you’ve bought with your MetaMask account. If you bought tokens using a different account, those tokens will not appear here."
+ },
+ "total": {
+ "message": "Total"
+ },
+ "transactions": {
+ "message": "transactions"
+ },
+ "transactionMemo": {
+ "message": "Transaction memo (optional)"
+ },
+ "transactionNumber": {
+ "message": "Transaction Number"
+ },
+ "transfers": {
+ "message": "Transfers"
+ },
+ "troubleTokenBalances": {
+ "message": "We had trouble loading your token balances. You can view them ",
+ "description": "Followed by a link (here) to view token balances"
+ },
+ "twelveWords": {
+ "message": "These 12 words are the only way to restore your MetaMask accounts.\nSave them somewhere safe and secret."
+ },
+ "typePassword": {
+ "message": "Type Your Password"
+ },
+ "uiWelcome": {
+ "message": "Welcome to the New UI (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "You are now using the new Metamask UI. Take a look around, try out new features like sending tokens, and let us know if you have any issues."
+ },
+ "unapproved": {
+ "message": "Unapproved"
+ },
+ "unavailable": {
+ "message": "Unavailable"
+ },
+ "unknown": {
+ "message": "Unknown"
+ },
+ "unknownNetwork": {
+ "message": "Unknown Private Network"
+ },
+ "unknownNetworkId": {
+ "message": "Unknown network ID"
+ },
+ "uriErrorMsg": {
+ "message": "URIs require the appropriate HTTP/HTTPS prefix."
+ },
+ "usaOnly": {
+ "message": "USA only",
+ "description": "Using this exchange is limited to people inside the USA"
+ },
+ "usedByClients": {
+ "message": "Used by a variety of different clients"
+ },
+ "useOldUI": {
+ "message": "Use old UI"
+ },
+ "validFileImport": {
+ "message": "You must select a valid file to import."
+ },
+ "vaultCreated": {
+ "message": "Vault Created"
+ },
+ "viewAccount": {
+ "message": "View Account"
+ },
+ "visitWebSite": {
+ "message": "Visit our web site"
+ },
+ "warning": {
+ "message": "Warning"
+ },
+ "welcomeBeta": {
+ "message": "Welcome to MetaMask Beta"
+ },
+ "whatsThis": {
+ "message": "What's this?"
+ },
+ "yourSigRequested": {
+ "message": "Your signature is being requested"
+ },
+ "youSign": {
+ "message": "You are signing"
}
}
diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json
index 78fc64dbf..aa2701c2c 100644
--- a/app/_locales/es/messages.json
+++ b/app/_locales/es/messages.json
@@ -1,10 +1,888 @@
{
+ "accept": {
+ "message": "Aceptar"
+ },
+ "account": {
+ "message": "Cuenta"
+ },
+ "accountDetails": {
+ "message": "Detalles de la cuenta"
+ },
+ "accountName": {
+ "message": "Nombre de la cuenta"
+ },
+ "addCustomToken": {
+ "message": "Agregar token personalizados"
+ },
+ "addToken": {
+ "message": "Agregar token"
+ },
+ "addTokens": {
+ "message": "Agregar tokens"
+ },
+ "address": {
+ "message": "Dirección"
+ },
+ "amount": {
+ "message": "Cantidad"
+ },
+ "amountPlusGas": {
+ "message": "Cantidad + Gas"
+ },
+ "appDescription": {
+ "message": "Extensión del navegador para Ethereum",
+ "description": "La descripción de la aplicación"
+ },
"appName": {
"message": "MetaMask",
- "description": "The name of the application"
+ "description": "El nombre de la aplicación"
},
- "appDescription": {
- "message": "Administración de identidad en Ethereum",
- "description": "The description of the application"
+ "approved": {
+ "message": "Aprobado"
+ },
+ "attemptingConnect": {
+ "message": "Intentando conectar a la Blockchain"
+ },
+ "attributions": {
+ "message": "Atribuciones"
+ },
+ "available": {
+ "message": "Disponible"
+ },
+ "back": {
+ "message": "Atrás"
+ },
+ "balance": {
+ "message": "Saldo"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Saldo de gas insuficiente"
+ },
+ "balances": {
+ "message": "Tus saldos"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "Debe ser mayor o igual a $1 y menor o igual a $2",
+ "description": "helper para ingresar hex como un ingreso decimal"
+ },
+ "blockiesIdenticon": {
+ "message": "Usar Blockies Identicon (Iconos)"
+ },
+ "borrowDharma": {
+ "message": "Pedir prestado con Dharma (Beta)"
+ },
+ "builtInCalifornia": {
+ "message": "Metamask fue diseñado y construido en California "
+ },
+ "buy": {
+ "message": "Comprar"
+ },
+ "buyCoinbase": {
+ "message": "Comprar en Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase es la forma global más popular para comprar y vender bitcoin, ethereum y litecoin"
+ },
+ "cancel": {
+ "message": "Cancelar"
+ },
+ "classicInterface": {
+ "message": "Usar interfaz clásica "
+ },
+ "clickCopy": {
+ "message": "Click para copiar"
+ },
+ "confirm": {
+ "message": "Confirmar"
+ },
+ "confirmContract": {
+ "message": "Confirmar contrato"
+ },
+ "confirmed": {
+ "message": "Confirmado"
+ },
+ "confirmPassword": {
+ "message": "Confirmar contraseña"
+ },
+ "confirmTransaction": {
+ "message": "Confirmar transacción"
+ },
+ "connectingToMainnet": {
+ "message": "Conectando a la red principal de Ethereum (Main Net)"
+ },
+ "connectingToRopsten": {
+ "message": "Conectando a la red de test Ropsten"
+ },
+ "connectingToKovan": {
+ "message": "Conectando a la red de test Kovan"
+ },
+ "connectingToRinkeby": {
+ "message": "Conectando a la red de test Rinkeby"
+ },
+ "connectingToUnknown": {
+ "message": "Conectando a una red desconocida"
+ },
+ "continue": {
+ "message": "Continuar"
+ },
+ "continueToCoinbase": {
+ "message": "Continuar a Coinbase"
+ },
+ "contractDeployment": {
+ "message": "Desplegar (Deploy) contrato"
+ },
+ "conversionProgress": {
+ "message": "Conversión en progreso"
+ },
+ "copiedButton": {
+ "message": "Copiado"
+ },
+ "copiedClipboard": {
+ "message": "Copiado al portapapeles"
+ },
+ "copiedExclamation": {
+ "message": "Copiado!"
+ },
+ "copiedSafe": {
+ "message": "Ya lo guardé en un lugar seguro"
+ },
+ "copy": {
+ "message": "Copiar"
+ },
+ "copyButton": {
+ "message": " Copiar "
+ },
+ "copyPrivateKey": {
+ "message": "Esta es tu llave privada (Click para copiar)"
+ },
+ "copyToClipboard": {
+ "message": "Copiar al portapapeles"
+ },
+ "create": {
+ "message": "Crear"
+ },
+ "createAccount": {
+ "message": "Crear Cuenta"
+ },
+ "createDen": {
+ "message": "Crear"
+ },
+ "crypto": {
+ "message": "Crypto",
+ "description": "Tipo de Cambio (criptomonedas)"
+ },
+ "currentConversion": {
+ "message": "Conversión Actual"
+ },
+ "currentNetwork": {
+ "message": "Red actual"
+ },
+ "currentRpc": {
+ "message": "RPC actual"
+ },
+ "customGas": {
+ "message": "Personalizar Gas"
+ },
+ "customRPC": {
+ "message": "RPC Personalizado"
+ },
+ "customize": {
+ "message": "Personalizar"
+ },
+ "decimal": {
+ "message": "Decimales de precisión"
+ },
+ "decimalsMustZerotoTen": {
+ "message": "Los decimales deben ser al menos 0 y no más de 36"
+ },
+ "defaultNetwork": {
+ "message": "La red por defecto para las transacciones de Ether es MainNet (red principal)"
+ },
+ "denExplainer": {
+ "message": "Tu DEN es tu contraseña encriptada guardada dentro de MetaMask"
+ },
+ "deposit": {
+ "message": "Depositar"
+ },
+ "depositBTC": {
+ "message": "Deposita tus BTC a la dirección de abajo:"
+ },
+ "depositCoin": {
+ "message": "Deposita tu $1 a la dirección de abajo",
+ "description": "Informa al usuario que moneda ha elegido para depositar en shapeshift"
+ },
+ "depositEth": {
+ "message": "Depositar Ether"
+ },
+ "depositEther": {
+ "message": "Depositar Ether"
+ },
+ "depositFiat": {
+ "message": "Depositar con Fiat (divisa nacional)"
+ },
+ "depositFromAccount": {
+ "message": "Depositar con otra cuenta"
+ },
+ "depositShapeShift": {
+ "message": "Depositar con ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "Si tu tienes otras criptomonedas, puedes intercambiar y depositar Ether directamente en tu billetera de MetaMask. No necesitas tener una cuenta"
+ },
+ "details": {
+ "message": "Detalles"
+ },
+ "directDeposit": {
+ "message": "Depósito directo"
+ },
+ "directDepositEther": {
+ "message": "Depositar Ether directamente"
+ },
+ "directDepositEtherExplainer": {
+ "message": "Si tu tienes algo de Ether, la forma rápida para tener Ether en tu nueva billetera es depositando directamente"
+ },
+ "done": {
+ "message": "Completo"
+ },
+ "downloadStatelogs": {
+ "message": "Descargar logs de estado"
+ },
+ "dropped": {
+ "message": "Caído"
+ },
+ "edit": {
+ "message": "Editar"
+ },
+ "editAccountName": {
+ "message": "Editar el nombre de la cuenta"
+ },
+ "emailUs": {
+ "message": "Envíanos un correo!"
+ },
+ "encryptNewDen": {
+ "message": "Encriptar tu nuevo DEN"
+ },
+ "enterPassword": {
+ "message": "Ingresa contraseña"
+ },
+ "enterPasswordConfirm": {
+ "message": "Ingresa tu contraseña para confirmar"
+ },
+ "etherscanView": {
+ "message": "Ver la cuenta en Etherscan"
+ },
+ "exchangeRate": {
+ "message": "Tipo de cambio"
+ },
+ "exportPrivateKey": {
+ "message": "Exportar llave privada"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "Exportar llaves privadas bajo TU PROPIO riesgo"
+ },
+ "failed": {
+ "message": "Fallo"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Tipo de cambio"
+ },
+ "fileImportFail": {
+ "message": "No funciona importar el archivo? Haz Click Aquí!",
+ "description": "Ayuda al usuario a importar su cuenta desde un archivo JSON"
+ },
+ "followTwitter": {
+ "message": "Síguenos en Twitter"
+ },
+ "from": {
+ "message": "De:"
+ },
+ "fromShapeShift": {
+ "message": "De ShapeShift"
+ },
+ "fromToSame": {
+ "message": "La dirección de origen y destino no pueden ser la misma"
+ },
+ "gas": {
+ "message": "Gas",
+ "description": "Indicación pequeña del costo de gas"
+ },
+ "gasFee": {
+ "message": "Comisión de gas"
+ },
+ "gasLimit": {
+ "message": "Límite de gas"
+ },
+ "gasLimitCalculation": {
+ "message": "Calculamos el límite de gas sugerido en función de las tasas de éxito de la red"
+ },
+ "gasLimitRequired": {
+ "message": "Límite de Gas requerido"
+ },
+ "gasLimitTooLow": {
+ "message": "El límite de gas debe ser de al menos 21000"
+ },
+ "gasPrice": {
+ "message": "Precio del Gas (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "Calculamos los precios sugeridos del gas en función de las tasas de éxito de la red"
+ },
+ "gasPriceRequired": {
+ "message": "Precio del gas requerido"
+ },
+ "generatingSeed": {
+ "message": "Generando semilla..."
+ },
+ "getEther": {
+ "message": "Conseguir Ether"
+ },
+ "getEtherFromFaucet": {
+ "message": "Obtenga Ether de un faucet (grifo) por $1",
+ "description": "Muestra el nombre de la red para el faucet (grifo) de Ether"
+ },
+ "greaterThanMin": {
+ "message": "Debe ser mayor o igual a $1",
+ "description": "helper para ingresar hex como entrada decimal"
+ },
+ "here": {
+ "message": "Aquí",
+ "description": "como en -haz click aquí- para más información"
+ },
+ "hereList": {
+ "message": "Aquí está una lista!!!"
+ },
+ "hide": {
+ "message": "Ocultar"
+ },
+ "hideToken": {
+ "message": "Ocultar Token"
+ },
+ "hideTokenPrompt": {
+ "message": "Ocultar Token?"
+ },
+ "holdEther": {
+ "message": "Te permite mantener tus ether y tokens, así como puente para aplicaciones descentralizadas"
+ },
+ "howToDeposit": {
+ "message": "Cómo te gustaria depositar Ether?"
+ },
+ "import": {
+ "message": "Importar",
+ "description": "Botón para importar una cuenta desde un archivo seleccionado"
+ },
+ "importAccount": {
+ "message": "Importar cuenta"
+ },
+ "importAnAccount": {
+ "message": "Importar una cuenta"
+ },
+ "importDen": {
+ "message": "Importar DEN existente"
+ },
+ "imported": {
+ "message": "Importado",
+ "description": "Estado que muestra que una cuenta ha sido completamente cargada en el llavero"
+ },
+ "importAccountMsg": {
+ "message": "Cuentas importadas no serán asociadas con tu cuenta original creada con tu MetaMask. Aprende más acerca de importar cuentas."
+ },
+ "info": {
+ "message": "Información"
+ },
+ "infoHelp": {
+ "message": "Informacion y Ayuda"
+ },
+ "insufficientFunds": {
+ "message": "Fondos insuficientes"
+ },
+ "insufficientTokens": {
+ "message": "Tokens insuficientes"
+ },
+ "invalidAddress": {
+ "message": "Dirección inválida"
+ },
+ "invalidAddressRecipient": {
+ "message": "Dirección del destinatario invalida"
+ },
+ "invalidGasParams": {
+ "message": "Parametros de gas inválidos"
+ },
+ "invalidInput": {
+ "message": "Entrada inválida"
+ },
+ "invalidRPC": {
+ "message": "Invalida URL del RPC"
+ },
+ "invalidRequest": {
+ "message": "Petición inválida"
+ },
+ "jsonFail": {
+ "message": "Algo malo pasó. Asegurate que tu JSON tiene el formato correcto"
+ },
+ "jsonFile": {
+ "message": "Archivo JSON",
+ "description": "Formato para importar una cuenta"
+ },
+ "knowledgeDataBase": {
+ "message": "Visita nuestra base de conocimiento"
+ },
+ "kovan": {
+ "message": "Red de pruebas Kovan"
+ },
+ "lessThanMax": {
+ "message": "Debe ser menor o igual a $1",
+ "description": "Helper para ingresar hex como decimal"
+ },
+ "likeToAddTokens": {
+ "message": "¿Te gustaría agregar estos tokens?"
+ },
+ "limit": {
+ "message": "Límite"
+ },
+ "links": {
+ "message": "Enlaces"
+ },
+ "loading": {
+ "message": "Cargando..."
+ },
+ "loadingTokens": {
+ "message": "Cargando tokens..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "login": {
+ "message": "Ingresar"
+ },
+ "logout": {
+ "message": "Cerrar sesión"
+ },
+ "loose": {
+ "message": "Suelto"
+ },
+ "loweCaseWords": {
+ "message": "las frases semilla sólo pueden tener minúsculas"
+ },
+ "mainnet": {
+ "message": "Red principal de Ethereum (Main Net)"
+ },
+ "max": {
+ "message": "Max"
+ },
+ "message": {
+ "message": "Mensaje"
+ },
+ "metamaskDescription": {
+ "message": "Metamask es una identidad segura en Ethereum"
+ },
+ "min": {
+ "message": "Mínimo"
+ },
+ "mustSelectOne": {
+ "message": "Debe seleccionar al menos un (1) token"
+ },
+ "myAccounts": {
+ "message": "Mis cuentas"
+ },
+ "needEtherInWallet": {
+ "message": "Para interactuar con una aplicación descentralizada usando MetaMask, vas a necesitar tener Ether en tu billetera"
+ },
+ "needImportFile": {
+ "message": "Debes seleccionar un archivo para importar",
+ "description": "El usuario está importando una cuenta y necesita agregar un archivo para continuar"
+ },
+ "needImportPassword": {
+ "message": "Debes ingresar una contraseña para el archivo seleccionado",
+ "description": "Contraseña y archivo necesarios para importar una cuenta"
+ },
+ "negativeETH": {
+ "message": "No se pueden mandar cantidades negativas de ETH"
+ },
+ "networks": {
+ "message": "Redes"
+ },
+ "newAccount": {
+ "message": "Nueva cuenta"
+ },
+ "newAccountNumberName": {
+ "message": "Cuenta $1",
+ "description": "Nombre por defecto de la próxima cuenta a ser creada o pantalla de creación de cuenta"
+ },
+ "newContract": {
+ "message": "Nuevo contrato"
+ },
+ "newPassword": {
+ "message": "Nueva contraseña (mínimo [8] caracteres)"
+ },
+ "newPassword8Chars": {
+ "message": "Nueva contraseña (mínimo [8] caracteres)"
+ },
+ "newRPC": {
+ "message": "Nueva URL del RPC"
+ },
+ "newRecipient": {
+ "message": "Nuevo destinatario"
+ },
+ "next": {
+ "message": "Siguiente"
+ },
+ "noAddressForName": {
+ "message": "No se ha establecido ninguna dirección para este nombre"
+ },
+ "noDeposits": {
+ "message": "No hay depósitos recibidos"
+ },
+ "noTransactionHistory": {
+ "message": "Sin historial de transacciones"
+ },
+ "noTransactions": {
+ "message": "Sin transacciones"
+ },
+ "notStarted": {
+ "message": "Sin iniciar"
+ },
+ "ok": {
+ "message": "Ok"
+ },
+ "oldUI": {
+ "message": "Antigua UI (Interfaz de Usuario)"
+ },
+ "oldUIMessage": {
+ "message": "Regresaste a la antigua UI (Interfaz de Usuario). Puedes regresar a la nueva UI mediante la opcion en la barra desplegable del menu de arriba a la derecha."
+ },
+ "onlySendToEtherAddress": {
+ "message": "Sólo envía a una dirección de Ethereum"
+ },
+ "or": {
+ "message": "o",
+ "description": "opción entre crear o importar una cuenta"
+ },
+ "passwordCorrect": {
+ "message": "Asegurate que tu contraseña es correcta"
+ },
+ "passwordMismatch": {
+ "message": "Contraseña no coincide",
+ "description": "En el proceso de creación de contraseña, los dos campos de contraseña no coincidieron"
+ },
+ "passwordNotLongEnough": {
+ "message": "La contraseña no es lo suficientemente larga"
+ },
+ "passwordsDontMatch": {
+ "message": "Contraseñas no coinciden"
+ },
+ "passwordShort": {
+ "message": "Contraseña no es lo suficientemente larga",
+ "description": "En el proceso de creación de contraseña, esta no es lo suficientemente larga para ser segura"
+ },
+ "pastePrivateKey": {
+ "message": "Pega tu llave privada aqui",
+ "description": "Para importar una cuenta desde una llave privada"
+ },
+ "pasteSeed": {
+ "message": "Pega tu frase semilla aquí!"
+ },
+ "personalAddressDetected": {
+ "message": "Dirección personal detectada. Ingresa la dirección del contrato del token"
+ },
+ "pleaseReviewTransaction": {
+ "message": "Por favor revisa tu transaccion"
+ },
+ "privacyMsg": {
+ "message": "Política de privacidad"
+ },
+ "privateKey": {
+ "message": "Llave privada",
+ "description": "Selecciona este tupo de archivo para importar una cuenta"
+ },
+ "privateKeyWarning": {
+ "message": "Advertencia: NUNCA reveles esta clave. Cualquier persona con tus claves privadas puede robar los activos retenidos en tu cuenta"
+ },
+ "privateNetwork": {
+ "message": "Red Privada"
+ },
+ "qrCode": {
+ "message": "Mostrar codigo QR"
+ },
+ "readMore": {
+ "message": "Leer más aquí"
+ },
+ "readMore2": {
+ "message": "Leer más"
+ },
+ "readdToken": {
+ "message": "Puede volver a agregar este token en el futuro yendo a 'Agregar token' en el menú de opciones de tu cuenta"
+ },
+ "receive": {
+ "message": "Recibir"
+ },
+ "recipientAddress": {
+ "message": "Dirección del receptor"
+ },
+ "refundAddress": {
+ "message": "Tu dirección de reembolso"
+ },
+ "rejected": {
+ "message": "Rechazado"
+ },
+ "required": {
+ "message": "Requerido"
+ },
+ "resetAccount": {
+ "message": "Reiniciar cuenta"
+ },
+ "restoreFromSeed": {
+ "message": "Restaurar desde semilla"
+ },
+ "restoreVault": {
+ "message": "Restaurar Bóveda"
+ },
+ "retryWithMoreGas": {
+ "message": "Vuelva a intentar con un precio de Gas más alto aquí"
+ },
+ "revealSeedWords": {
+ "message": "Revelar palabras de semilla"
+ },
+ "revealSeedWordsWarning": {
+ "message": "No recuperes tu semilla en un lugar publico! Esas palabras pueden ser usadas para robarte todas tus cuentas"
+ },
+ "revert": {
+ "message": "Revertir"
+ },
+ "rinkeby": {
+ "message": "Red privada Rinkeby"
+ },
+ "ropsten": {
+ "message": "Red privada Ropsten"
+ },
+ "sampleAccountName": {
+ "message": "Ej. Mi nueva cuenta",
+ "description": "Ayuda al usuario a entender el concepto de agregar un nombre, leíble por humanos, a su cuenta"
+ },
+ "save": {
+ "message": "Guardar"
+ },
+ "saveAsFile": {
+ "message": "Guardar como archivo",
+ "description": "Proceso de exportación de cuenta"
+ },
+ "saveSeedAsFile": {
+ "message": "Guardar la semilla como archivo"
+ },
+ "search": {
+ "message": "Buscar"
+ },
+ "secretPhrase": {
+ "message": "Ingresa tu frase de doce (12) palabras para restaurar tu bóveda"
+ },
+ "seedPhraseReq": {
+ "message": "las frases semilla tienen doce (12) palabras de largo"
+ },
+ "select": {
+ "message": "Seleccionar"
+ },
+ "selectCurrency": {
+ "message": "Seleccionar moneda"
+ },
+ "selectService": {
+ "message": "Seleccionar servicio"
+ },
+ "selectType": {
+ "message": "Seleccionar tipo"
+ },
+ "send": {
+ "message": "Enviar"
+ },
+ "sendETH": {
+ "message": "Enviar Ether"
+ },
+ "sendTokens": {
+ "message": "Enviar Tokens"
+ },
+ "sendTokensAnywhere": {
+ "message": "Enviar Tokens a cualquiera con una cuenta de Ethereum"
+ },
+ "settings": {
+ "message": "Configuración"
+ },
+ "shapeshiftBuy": {
+ "message": "Comprar con ShapeShift"
+ },
+ "showPrivateKeys": {
+ "message": "Mostrar llaves privadas"
+ },
+ "showQRCode": {
+ "message": "Mostrar codigo QR"
+ },
+ "sigRequest": {
+ "message": "Solicitud de firma"
+ },
+ "sigRequested": {
+ "message": "Firma solicitada"
+ },
+ "sign": {
+ "message": "Firmar"
+ },
+ "signed": {
+ "message": "Firmado"
+ },
+ "signMessage": {
+ "message": "Firmar Mensaje"
+ },
+ "signNotice": {
+ "message": "Firmar este mensaje puede tener\n efectos secundarios peligrosos. Firma sólo\nmensajes desde sitios a los cuales tú estés dispuesto a confiar completamente tu cuenta.\nEste método peligroso va a ser \nremovido en una version futura."
+ },
+ "spaceBetween": {
+ "message": "Sólo puede haber un espacio entre las palabras"
+ },
+ "stateLogs": {
+ "message": "Logs de estado"
+ },
+ "stateLogsDescription": {
+ "message": "Los Logs de estado contienen tus direcciones de cuentas públicas y transacciones envíadas"
+ },
+ "stateLogError": {
+ "message": "Error en la recogida de logs de estado"
+ },
+ "status": {
+ "message": "Estado"
+ },
+ "submit": {
+ "message": "Enviar"
+ },
+ "submitted": {
+ "message": "Enviado"
+ },
+ "supportCenter": {
+ "message": "Visita nuestro centro de atención"
+ },
+ "symbolBetweenZeroTen": {
+ "message": "Símbolo debe ser entre 0 y 10 caracteres"
+ },
+ "takesTooLong": {
+ "message": "¿Está tomando demasiado?"
+ },
+ "terms": {
+ "message": "Terminos de Uso"
+ },
+ "testFaucet": {
+ "message": "Probar Faucet"
+ },
+ "to": {
+ "message": "Para:"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 a ETH via ShapeShift",
+ "description": "el sistema llenará el tipo de depósito al principio del mensaje"
+ },
+ "tokenAddress": {
+ "message": "Dirección del token"
+ },
+ "tokenAlreadyAdded": {
+ "message": "El token esta actualmente agregado"
+ },
+ "tokenBalance": {
+ "message": "Tu balance de tokens es:"
+ },
+ "tokenSelection": {
+ "message": "Busca tokens o selecciónalo de nuestra lista de tokens populares"
+ },
+ "tokenSymbol": {
+ "message": "Símbolo del token"
+ },
+ "tokenWarning1": {
+ "message": "Manten un registro de los tokens que has comprado con tu cuenta de MetaMask. Si compraste tokens usando una cuenta diferente, esos tokens no aparecerán aquí."
+ },
+ "total": {
+ "message": "Total"
+ },
+ "transactionMemo": {
+ "message": "Memo de transaccion (opcional)"
+ },
+ "transactionNumber": {
+ "message": "Número de transacción"
+ },
+ "transactions": {
+ "message": "Transacciones"
+ },
+ "transfers": {
+ "message": "Transferencias"
+ },
+ "troubleTokenBalances": {
+ "message": "Tuvimos problemas para cargar tus saldos de tokens. Puedes verlos ",
+ "description": "Seguidos por un enlace (aquí) para ver los saldos de token"
+ },
+ "twelveWords": {
+ "message": "Estas 12 palabras son la única forma de restablecer tus cuentas de MetaMask. \nGuardalas en un lugar seguro y secreto."
+ },
+ "typePassword": {
+ "message": "Escribe tu contraseña"
+ },
+ "uiWelcome": {
+ "message": "Bienvenido a la nueva UI (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "Estás usando la nueva UI de MetaMask. Echa un vistazo alrededor, prueba las nuevas características, tales como mandar tokens, y déjanos saber si tienes algún problema"
+ },
+ "unavailable": {
+ "message": "No disponible"
+ },
+ "unapproved": {
+ "message": "No Aprobado"
+ },
+ "unknown": {
+ "message": "Desconocido (a)"
+ },
+ "unknownNetwork": {
+ "message": "Red privada desconocida"
+ },
+ "unknownNetworkId": {
+ "message": "ID (identidad) de Red desconocida"
+ },
+ "uriErrorMsg": {
+ "message": "URI necesita el prefijo HTTP/HTTPS apropiado"
+ },
+ "usaOnly": {
+ "message": "Sólo USA (Estados Unidos)",
+ "description": "El uso de este exchange (casa de cambio) está limitado a las personas dentro de los Estados Unidos de America"
+ },
+ "useOldUI": {
+ "message": "Usar UI antigua"
+ },
+ "usedByClients": {
+ "message": "Utilizado por una variedad de clientes diferentes"
+ },
+ "validFileImport": {
+ "message": "Debes selecionar un archivo valido para importar"
+ },
+ "vaultCreated": {
+ "message": "Bóveda creada"
+ },
+ "viewAccount": {
+ "message": "Mirar cuenta"
+ },
+ "visitWebSite": {
+ "message": "Visita nuestro sitio web"
+ },
+ "walletSeed": {
+ "message": "Semilla de la billetera"
+ },
+ "warning": {
+ "message": "Advertencia"
+ },
+ "welcomeBeta": {
+ "message": "Bienvenido a Metamask Beta"
+ },
+ "whatsThis": {
+ "message": "Qué es esto?"
+ },
+ "youSign": {
+ "message": "Usted está firmando"
+ },
+ "yourSigRequested": {
+ "message": "Tu firma ya fue solicitada"
}
}
diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json
new file mode 100644
index 000000000..da2cfe4f8
--- /dev/null
+++ b/app/_locales/fr/messages.json
@@ -0,0 +1,609 @@
+{
+ "accept": {
+ "message": "Accepter"
+ },
+ "account": {
+ "message": "Compte"
+ },
+ "accountDetails": {
+ "message": "Détails du compte"
+ },
+ "accountName": {
+ "message": "Nom du compte"
+ },
+ "address": {
+ "message": "Adresse"
+ },
+ "addToken": {
+ "message": "Ajouter un jeton"
+ },
+ "amount": {
+ "message": "Montant"
+ },
+ "amountPlusGas": {
+ "message": "Montant + Gaz"
+ },
+ "appDescription": {
+ "message": "Extension Ethereum pour navigateur",
+ "description": "La description de l'application"
+ },
+ "appName": {
+ "message": "MetaMask",
+ "description": "Le nom de l'application"
+ },
+ "attemptingConnect": {
+ "message": "Tentative de connexion à blockchain."
+ },
+ "available": {
+ "message": "Disponible"
+ },
+ "back": {
+ "message": "Retour"
+ },
+ "balance": {
+ "message": "Balance:"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Solde insuffisant pour le total actuel de gaz"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "doit être supérieur ou égal à $1 et inférieur ou égal à $2",
+ "description": "helper pour la saisie hexadécimale en entrée décimale"
+ },
+ "borrowDharma": {
+ "message": "Emprunter avec Dharma (Bêta)"
+ },
+ "buy": {
+ "message": "Acheter"
+ },
+ "buyCoinbase": {
+ "message": "Acheter sur Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase est le moyen le plus populaire au monde d'acheter et de vendre du bitcoin, de l'ethereum et du litecoin."
+ },
+ "cancel": {
+ "message": "Annuler"
+ },
+ "clickCopy": {
+ "message": "Cliquer pour copier"
+ },
+ "confirm": {
+ "message": "Confirmer"
+ },
+ "confirmContract": {
+ "message": "Confirmer le contrat"
+ },
+ "confirmPassword": {
+ "message": "Confirmer le mot de passe"
+ },
+ "confirmTransaction": {
+ "message": "Confirmer la transaction"
+ },
+ "continueToCoinbase": {
+ "message": "Continuer vers Coinbase"
+ },
+ "contractDeployment": {
+ "message": "Déploiement de contrat"
+ },
+ "conversionProgress": {
+ "message": "Conversion en cours"
+ },
+ "copiedButton": {
+ "message": "Copié"
+ },
+ "copiedClipboard": {
+ "message": "Copié dans le Presse-papiers"
+ },
+ "copiedExclamation": {
+ "message": "Copié!"
+ },
+ "copy": {
+ "message": "Copier"
+ },
+ "copyToClipboard": {
+ "message": "Copier dans le presse-papier"
+ },
+ "copyButton": {
+ "message": " Copier "
+ },
+ "copyPrivateKey": {
+ "message": "Ceci est votre clé privée (cliquez pour copier)"
+ },
+ "create": {
+ "message": "Créer"
+ },
+ "createAccount": {
+ "message": "Créer un compte"
+ },
+ "createDen": {
+ "message": "Créer"
+ },
+ "crypto": {
+ "message": "Crypto",
+ "description": "Type d'échange (cryptocurrencies)"
+ },
+ "customGas": {
+ "message": "Personnaliser le Gaz"
+ },
+ "customize": {
+ "message": "Personnaliser"
+ },
+ "customRPC": {
+ "message": "RPC personnalisé"
+ },
+ "defaultNetwork": {
+ "message": "Le réseau par défaut pour les transactions Ether est Main Net."
+ },
+ "denExplainer": {
+ "message": "Votre DEN est votre stockage crypté par mot de passe dans MetaMask."
+ },
+ "deposit": {
+ "message": "Dépôt"
+ },
+ "depositBTC": {
+ "message": "Déposez vos BTC à l'adresse ci-dessous:"
+ },
+ "depositCoin": {
+ "message": "Déposez votre $1 à l'adresse ci-dessous",
+ "description": "Indique à l'utilisateur quelle monnaie il a choisi de déposer avec shapeshift"
+ },
+ "depositEth": {
+ "message": "Dépôt Eth"
+ },
+ "depositEther": {
+ "message": "Dépôt Ether"
+ },
+ "depositFiat": {
+ "message": "Dépôt de monnaie-fiat"
+ },
+ "depositFromAccount": {
+ "message": "Dépôt d'un autre compte"
+ },
+ "depositShapeShift": {
+ "message": "Déposez avec ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "Si vous possédez d'autres crypto-monnaies, vous pouvez échanger et déposer de l'Ether directement dans votre portefeuille MetaMask. Aucun compte n'est requis."
+ },
+ "details": {
+ "message": "Détails"
+ },
+ "directDeposit": {
+ "message": "Dépôt direct"
+ },
+ "directDepositEther": {
+ "message": "Dépôt direct d'Ether"
+ },
+ "directDepositEtherExplainer": {
+ "message": "Si vous avez déjà de l'Ether, le moyen le plus rapide d'obtenir des Ether dans votre nouveau portefeuille est par dépôt direct."
+ },
+ "done": {
+ "message": "Fait"
+ },
+ "edit": {
+ "message": "Modifier"
+ },
+ "editAccountName": {
+ "message": "Modifier le nom du compte"
+ },
+ "encryptNewDen": {
+ "message": "Chiffrer votre nouveau DEN"
+ },
+ "enterPassword": {
+ "message": "Entrer le mot de passe"
+ },
+ "etherscanView": {
+ "message": "Afficher le compte sur Etherscan"
+ },
+ "exchangeRate": {
+ "message": "Taux de change"
+ },
+ "exportPrivateKey": {
+ "message": "Exporter la clé privée"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "Exporter les clés privées à vos risques et périls."
+ },
+ "failed": {
+ "message": "Échec"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Type d'échange"
+ },
+ "fileImportFail": {
+ "message": "L'importation de fichier ne fonctionne pas? Cliquez ici!",
+ "description": "Aide l'utilisateur à importer son compte à partir d'un fichier JSON"
+ },
+ "from": {
+ "message": "de"
+ },
+ "fromShapeShift": {
+ "message": "ShapeShift de"
+ },
+ "gas": {
+ "message": "Gas",
+ "description": "Indication courte du coût du gaz"
+ },
+ "gasFee": {
+ "message": "Frais de gaz"
+ },
+ "gasLimit": {
+ "message": "Limite de gaz"
+ },
+ "gasLimitCalculation": {
+ "message": "Nous calculons la limite de gaz suggérée en fonction des taux de réussite du réseau."
+ },
+ "gasLimitRequired": {
+ "message": "Limite de gaz requise"
+ },
+ "gasLimitTooLow": {
+ "message": "La limite de gaz doit être d'au moins 21000"
+ },
+ "gasPrice": {
+ "message": "Prix du gaz (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "Nous calculons les prix du gaz proposés en fonction des taux de réussite du réseau."
+ },
+ "gasPriceRequired": {
+ "message": "Prix du gaz requis"
+ },
+ "getEther": {
+ "message": "Obtenir des Ether"
+ },
+ "getEtherFromFaucet": {
+ "message": "Obtenir de l'Ether d'une faucet pour $1",
+ "description": "Affiche le nom du réseau pour la faucet d'Ether"
+ },
+ "greaterThanMin": {
+ "message": "doit être supérieur ou égal à $1.",
+ "description": "helper pour la saisie hexadécimale en entrée décimale"
+ },
+ "here": {
+ "message": "ici",
+ "description": "comme dans -cliquer ici- pour plus d'informations (en rapport avec troubleTokenBalances)"
+ },
+ "hide": {
+ "message": "Cacher"
+ },
+ "hideToken": {
+ "message": "Masquer le jeton"
+ },
+ "hideTokenPrompt": {
+ "message": "Masquer le jeton?"
+ },
+ "howToDeposit": {
+ "message": "Comment voulez-vous déposer de l'Ether?"
+ },
+ "import": {
+ "message": "Importer",
+ "description": "Bouton pour importer un compte à partir d'un fichier sélectionné"
+ },
+ "importAccount": {
+ "message": "Importer compte"
+ },
+ "importAnAccount": {
+ "message": "Importer un compte"
+ },
+ "importDen": {
+ "message": "Importer DEN existant"
+ },
+ "imported": {
+ "message": "Importé",
+ "description": "statut indiquant qu'un compte a été entièrement chargé dans le trousseau de clés"
+ },
+ "infoHelp": {
+ "message": "Info & Aide"
+ },
+ "invalidAddress": {
+ "message": "Adresse invalide"
+ },
+ "invalidGasParams": {
+ "message": "Paramètres de gaz invalides"
+ },
+ "invalidInput": {
+ "message": "Entrée non valide."
+ },
+ "invalidRequest": {
+ "message": "Requête invalide"
+ },
+ "jsonFile": {
+ "message": "Fichier JSON",
+ "description": "format d'importation d'un compte"
+ },
+ "kovan": {
+ "message": "Réseau de test Kovan"
+ },
+ "lessThanMax": {
+ "message": "doit être inférieur ou égal à $1.",
+ "description": "helper pour la saisie hexadécimale en entrée décimale"
+ },
+ "limit": {
+ "message": "Limite"
+ },
+ "loading": {
+ "message": "Chargement..."
+ },
+ "loadingTokens": {
+ "message": "Chargement des jetons..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "logout": {
+ "message": "Déconnexion"
+ },
+ "loose": {
+ "message": "Vacant"
+ },
+ "mainnet": {
+ "message": "Réseau principal Ethereum"
+ },
+ "message": {
+ "message": "Message"
+ },
+ "min": {
+ "message": "Minimum"
+ },
+ "myAccounts": {
+ "message": "Mes comptes"
+ },
+ "needEtherInWallet": {
+ "message": "Pour interagir avec des applications décentralisées à l'aide de MetaMask, vous aurez besoin d'Ether dans votre portefeuille."
+ },
+ "needImportFile": {
+ "message": "Vous devez sélectionner un fichier à importer.",
+ "description": "L'utilisateur doit ajouter un fichier pour continuer"
+ },
+ "needImportPassword": {
+ "message": "Vous devez entrer un mot de passe pour le fichier sélectionné.",
+ "description": "Mot de passe et fichier requis pour importer un compte"
+ },
+ "networks": {
+ "message": "Réseaux"
+ },
+ "newAccount": {
+ "message": "Nouveau compte"
+ },
+ "newAccountNumberName": {
+ "message": "Compte $1",
+ "description": "Nom par défaut du compte suivant à créer sur l'écran de création de compte"
+ },
+ "newContract": {
+ "message": "Nouveau contrat"
+ },
+ "newPassword": {
+ "message": "Nouveau mot de passe (min 8 caractères)"
+ },
+ "newRecipient": {
+ "message": "Nouveau destinataire"
+ },
+ "next": {
+ "message": "Suivant"
+ },
+ "noAddressForName": {
+ "message": "Aucune adresse n'a été définie pour ce nom."
+ },
+ "noDeposits": {
+ "message": "Aucun dépôt reçu"
+ },
+ "noTransactionHistory": {
+ "message": "Aucun historique de transaction."
+ },
+ "noTransactions": {
+ "message": "Aucune transaction"
+ },
+ "notStarted": {
+ "message": "Pas démarré"
+ },
+ "oldUI": {
+ "message": "Ancienne interface utilisateur"
+ },
+ "oldUIMessage": {
+ "message": "Vous êtes revenu à l'ancienne interface utilisateur.Vous pouvez revenir à la nouvelle interface via l'option dans le menu déroulant en haut à droite."
+ },
+ "or": {
+ "message": "ou",
+ "description": "choix entre la création ou l'importation d'un nouveau compte"
+ },
+ "passwordMismatch": {
+ "message": "les mots de passe ne correspondent pas",
+ "description": "dans le processus de création de mot de passe, les deux nouveaux champs de mot de passe ne correspondent pas"
+ },
+ "passwordShort": {
+ "message": "mot de passe pas assez long",
+ "description": "dans le processus de création de mot de passe, le mot de passe n'est pas assez long pour être sécurisé"
+ },
+ "pastePrivateKey": {
+ "message": "Collez votre clé privée ici:",
+ "description": "Pour l'importation d'un compte à partir d'une clé privée"
+ },
+ "pasteSeed": {
+ "message": "Collez votre seed phrase ici!"
+ },
+ "pleaseReviewTransaction": {
+ "message": "Veuillez vérifier votre transaction."
+ },
+ "privateKey": {
+ "message": "Clé privée",
+ "description": "sélectionnez ce type de fichier à utiliser pour importer un compte"
+ },
+ "privateKeyWarning": {
+ "message": "Avertissement: Ne divulguez jamais cette clé, quiconque avec vos clés privées peut voler tous les actifs de votre compte."
+ },
+ "privateNetwork": {
+ "message": "Réseau privé"
+ },
+ "qrCode": {
+ "message": "Afficher le QR Code"
+ },
+ "readdToken": {
+ "message": "Vous pouvez ajouter ce jeton dans le futur en allant sur “Ajouter un jeton” dans le menu des options de votre compte."
+ },
+ "readMore": {
+ "message": "En savoir plus ici."
+ },
+ "receive": {
+ "message": "Recevoir"
+ },
+ "recipientAddress": {
+ "message": "Adresse du destinataire"
+ },
+ "refundAddress": {
+ "message": "Votre adresse de remboursement"
+ },
+ "rejected": {
+ "message": "Rejeté"
+ },
+ "required": {
+ "message": "Obligatoire"
+ },
+ "retryWithMoreGas": {
+ "message": "Réessayez avec un prix plus élevé du gaz ici"
+ },
+ "revert": {
+ "message": "Rétablir"
+ },
+ "rinkeby": {
+ "message": "Réseau de test Rinkeby"
+ },
+ "ropsten": {
+ "message": "Réseau de test Ropsten"
+ },
+ "sampleAccountName": {
+ "message": "Par exemple mon nouveau compte",
+ "description": "Aidez l'utilisateur à comprendre le concept d'ajout d'un nom lisible par un humain à son compte"
+ },
+ "save": {
+ "message": "Enregistrer"
+ },
+ "saveAsFile": {
+ "message": "Enregistrer dans un fichier",
+ "description": "Processus d'exportation de compte"
+ },
+ "selectService": {
+ "message": "Sélectionner un service"
+ },
+ "send": {
+ "message": "Envoyé"
+ },
+ "sendTokens": {
+ "message": "Envoyer des jetons"
+ },
+ "sendTokensAnywhere": {
+ "message": "Envoyer des jetons à toute personne possédant un compte Ethereum"
+ },
+ "settings": {
+ "message": "Paramètres"
+ },
+ "shapeshiftBuy": {
+ "message": "Acheter avec Shapeshift"
+ },
+ "showPrivateKeys": {
+ "message": "Afficher les clés privées"
+ },
+ "showQRCode": {
+ "message": "Afficher le QR Code"
+ },
+ "sign": {
+ "message": "Signer"
+ },
+ "signMessage": {
+ "message": "Signer le message"
+ },
+ "signNotice": {
+ "message": "La signature de ce message peut avoir des effets secondaires \ndangereux. Signer uniquement les messages de \nsites auxquels vous faites entièrement confiance avec votre compte.\n Cette méthode dangereuse sera supprimée dans une future version."
+ },
+ "sigRequest": {
+ "message": "Demande de signature"
+ },
+ "sigRequested": {
+ "message": "Signature demandée"
+ },
+ "status": {
+ "message": "Statut"
+ },
+ "submit": {
+ "message": "Soumettre"
+ },
+ "takesTooLong": {
+ "message": "Prend trop de temps?"
+ },
+ "testFaucet": {
+ "message": "Test Faucet"
+ },
+ "to": {
+ "message": "Destinataire"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 à ETH via ShapeShift",
+ "description": "le système remplira le type de dépôt au début du message"
+ },
+ "tokenBalance": {
+ "message": "Votre solde de jeton est:"
+ },
+ "total": {
+ "message": "Total"
+ },
+ "transactionMemo": {
+ "message": "Mémo de transaction (optionnel)"
+ },
+ "transactionNumber": {
+ "message": "Numéro de transaction"
+ },
+ "transfers": {
+ "message": "Transferts"
+ },
+ "troubleTokenBalances": {
+ "message": "Nous avons eu du mal à charger votre balance de jetons, vous pouvez la consulter ",
+ "description": "Suivi par un lien (ici) pour voir les soldes des jetons"
+ },
+ "typePassword": {
+ "message": "Entrez votre mot de passe"
+ },
+ "uiWelcome": {
+ "message": "Bienvenue dans la nouvelle interface utilisateur (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "Vous utilisez maintenant la nouvelle interface utilisateur Metamask. Jetez un coup d'oeil, essayez de nouvelles fonctionnalités comme l'envoi de jetons, et faites-nous savoir si vous avez des problèmes."
+ },
+ "unavailable": {
+ "message": "Indisponible"
+ },
+ "unknown": {
+ "message": "Inconnu"
+ },
+ "unknownNetwork": {
+ "message": "Réseau privé inconnu"
+ },
+ "unknownNetworkId": {
+ "message": "ID réseau inconnu"
+ },
+ "usaOnly": {
+ "message": "Etats-Unis seulement",
+ "description": "Utiliser cet échange est limité aux personnes à l'intérieur des Etats-Unis"
+ },
+ "usedByClients": {
+ "message": "Utilisé par une variété de clients différents"
+ },
+ "viewAccount": {
+ "message": "Afficher le compte"
+ },
+ "warning": {
+ "message": "Avertissement"
+ },
+ "whatsThis": {
+ "message": "Qu'est-ce que c'est?"
+ },
+ "yourSigRequested": {
+ "message": "Votre signature est demandée"
+ },
+ "youSign": {
+ "message": "Vous signez"
+ }
+}
diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json
new file mode 100644
index 000000000..f54ef98ca
--- /dev/null
+++ b/app/_locales/it/messages.json
@@ -0,0 +1,819 @@
+{
+ "accept": {
+ "message": "Accetta"
+ },
+ "account": {
+ "message": "Account"
+ },
+ "accountDetails": {
+ "message": "Dettagli Account"
+ },
+ "accountName": {
+ "message": "Nome Account"
+ },
+ "address": {
+ "message": "Indirizzo"
+ },
+ "addCustomToken": {
+ "message": "Aggiungi un token personalizzato"
+ },
+ "addToken": {
+ "message": "Aggiungi Token"
+ },
+ "addTokens": {
+ "message": "Aggiungi più token"
+ },
+ "amount": {
+ "message": "Importo"
+ },
+ "amountPlusGas": {
+ "message": "Importo + Gas"
+ },
+ "appDescription": {
+ "message": "Ethereum Browser Extension",
+ "description": "La descrizione dell'applicazione"
+ },
+ "appName": {
+ "message": "MetaMask",
+ "description": "Il nome dell'applicazione"
+ },
+ "attemptingConnect": {
+ "message": "Tentativo di connessione alla blockchain."
+ },
+ "attributions": {
+ "message": "Attribuzioni"
+ },
+ "available": {
+ "message": "Disponibile"
+ },
+ "back": {
+ "message": "Indietro"
+ },
+ "balance": {
+ "message": "Bilancio:"
+ },
+ "balances": {
+ "message": "I tuoi bilanci"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Bilancio insufficiente per il gas totale corrente"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "deve essere maggiore o uguale a $1 e minore o uguale a $2.",
+ "description": "aiuto per inserire un input esadecimale come decimale"
+ },
+ "blockiesIdenticon": {
+ "message": "Usa le icone Blockie"
+ },
+ "borrowDharma": {
+ "message": "Prendi in presisito con Dharma (Beta)"
+ },
+ "builtInCalifornia": {
+ "message": "MetaMask è progettato e costruito in California."
+ },
+ "buy": {
+ "message": "Compra"
+ },
+ "buyCoinbase": {
+ "message": "Compra su Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase è il servizio più popolare al mondo per comprare e vendere bitcoin, ethereum e litecoin."
+ },
+ "cancel": {
+ "message": "Cancella"
+ },
+ "classicInterface": {
+ "message": "Usa l'interfaccia classica"
+ },
+ "clickCopy": {
+ "message": "Clicca per Copiare"
+ },
+ "confirm": {
+ "message": "Conferma"
+ },
+ "confirmContract": {
+ "message": "Conferma Contratto"
+ },
+ "confirmPassword": {
+ "message": "Conferma Password"
+ },
+ "confirmTransaction": {
+ "message": "Conferma Transazione"
+ },
+ "continue": {
+ "message": "Continua"
+ },
+ "continueToCoinbase": {
+ "message": "Continua su Coinbase"
+ },
+ "contractDeployment": {
+ "message": "Distribuzione Contratto"
+ },
+ "conversionProgress": {
+ "message": "Conversione in corso"
+ },
+ "copiedButton": {
+ "message": "Copiato"
+ },
+ "copiedClipboard": {
+ "message": "Copiato negli Appunti"
+ },
+ "copiedExclamation": {
+ "message": "Copiato!"
+ },
+ "copiedSafe": {
+ "message": "Le ho copiate in un posto sicuro"
+ },
+ "copy": {
+ "message": "Copia"
+ },
+ "copyToClipboard": {
+ "message": "Copia negli appunti"
+ },
+ "copyButton": {
+ "message": " Copia "
+ },
+ "copyPrivateKey": {
+ "message": "Questa è la tua chiave privata (clicca per copiare)"
+ },
+ "create": {
+ "message": "Crea"
+ },
+ "createAccount": {
+ "message": "Crea Account"
+ },
+ "createDen": {
+ "message": "Crea"
+ },
+ "crypto": {
+ "message": "Crypto",
+ "description": "Tipo di exchange (cryptomonete)"
+ },
+ "currentConversion": {
+ "message": "Cambio Corrente"
+ },
+ "currentNetwork": {
+ "message": "Rete Corrente"
+ },
+ "customGas": {
+ "message": "Personalizza Gas"
+ },
+ "customize": {
+ "message": "Personalizza"
+ },
+ "customRPC": {
+ "message": "RPC Personalizzata"
+ },
+ "decimalsMustZerotoTen": {
+ "message": "Il numero di decimali deve essere almeno 0, e non oltre 36."
+ },
+ "decimal": {
+ "message": "Precisione Decimali"
+ },
+ "defaultNetwork": {
+ "message": "La rete predefinita per transazioni in Ether è la Rete Ethereum Principale."
+ },
+ "denExplainer": {
+ "message": "Il DEN è il tuo archivio crittato con password dentro Metamask."
+ },
+ "deposit": {
+ "message": "Deposita"
+ },
+ "depositBTC": {
+ "message": "Deposita i tuoi BTC all'indirizzo sotto:"
+ },
+ "depositCoin": {
+ "message": "Deposita $1 all'indirizzo sotto",
+ "description": "Dice all'utente quale moneta ha selezionato per depositare con Shapeshift"
+ },
+ "depositEth": {
+ "message": "Deposita Eth"
+ },
+ "depositEther": {
+ "message": "Deposita Ether"
+ },
+ "depositFiat": {
+ "message": "Deposita con moneta Fiat"
+ },
+ "depositFromAccount": {
+ "message": "Deposita da un altro account"
+ },
+ "depositShapeShift": {
+ "message": "Deposita con ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "Se possiedi altre criptomonete, puoi scambiare e depositare Ether direttamente nel tuo portafoglio MetaMask. Nessun account richiesto."
+ },
+ "details": {
+ "message": "Dettagli"
+ },
+ "directDeposit": {
+ "message": "Deposito Diretto"
+ },
+ "directDepositEther": {
+ "message": "Deposita Direttamente Ether"
+ },
+ "directDepositEtherExplainer": {
+ "message": "Se possiedi già degli Ether, questa è la via più veloce per aggiungere Ether al tuo portafoglio con un deposito diretto."
+ },
+ "done": {
+ "message": "Finito"
+ },
+ "downloadStatelogs": {
+ "message": "Scarica i log di Stato"
+ },
+ "edit": {
+ "message": "Modifica"
+ },
+ "editAccountName": {
+ "message": "Modifica Nome Account"
+ },
+ "emailUs": {
+ "message": "Mandaci una mail!"
+ },
+ "encryptNewDen": {
+ "message": "Cripta il tuo nuovo DEN"
+ },
+ "enterPassword": {
+ "message": "Inserisci password"
+ },
+ "enterPasswordConfirm": {
+ "message": "Inserisci la tua password per confermare"
+ },
+ "etherscanView": {
+ "message": "Vedi account su Etherscan"
+ },
+ "exchangeRate": {
+ "message": "Tasso di cambio"
+ },
+ "exportPrivateKey": {
+ "message": "Esporta Chiave Privata"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "Esporta chiave privata a tuo rischio."
+ },
+ "failed": {
+ "message": "Fallito"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Tipo di scambio"
+ },
+ "fileImportFail": {
+ "message": "Importazione file non funziona? Clicca qui!",
+ "description": "Aiuta gli utenti a importare il loro account da un file JSON"
+ },
+ "followTwitter": {
+ "message": "Seguici su Twitter"
+ },
+ "from": {
+ "message": "Da"
+ },
+ "fromToSame": {
+ "message": "Gli indirizzi Da e A non possono essere uguali"
+ },
+ "fromShapeShift": {
+ "message": "Da ShapeShift"
+ },
+ "gas": {
+ "message": "Gas",
+ "description": "Piccola indicazione del costo del gas"
+ },
+ "gasFee": {
+ "message": "Costo Gas"
+ },
+ "gasLimit": {
+ "message": "Gas Limite"
+ },
+ "gasLimitCalculation": {
+ "message": "Calcoliamo il gas limite suggerito in base al successo delle transazioni in rete."
+ },
+ "gasLimitRequired": {
+ "message": "Gas Limite Richiesto"
+ },
+ "gasLimitTooLow": {
+ "message": "Il Gas Limite deve essere almeno 21000"
+ },
+ "generatingSeed": {
+ "message": "Generando la frase seed..."
+ },
+ "gasPrice": {
+ "message": "Prezzo del Gas (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "Calcoliamo il gas limite suggerito in base al successo delle transazioni in rete."
+ },
+ "gasPriceRequired": {
+ "message": "Prezzo Gas Richiesto"
+ },
+ "getEther": {
+ "message": "Ottieni Ether"
+ },
+ "getEtherFromFaucet": {
+ "message": "Ottieni Get Ether da un faucet per $1",
+ "description": "Visualizza il nome della rete per il faucet Ether"
+ },
+ "greaterThanMin": {
+ "message": "deve essere maggiore o uguale a $1.",
+ "description": "aiuto per inserire un input esadecimale come decimale"
+ },
+ "here": {
+ "message": "qui",
+ "description": "per intendere -clicca qui- per maggiori informazioni (va con troubleTokenBalances)"
+ },
+ "hereList": {
+ "message": "Questa è una lista!!!!"
+ },
+ "hide": {
+ "message": "Nascondi"
+ },
+ "hideToken": {
+ "message": "Nascondi Token"
+ },
+ "hideTokenPrompt": {
+ "message": "Nascondi Token?"
+ },
+ "howToDeposit": {
+ "message": "Come vuoi depositare Ether?"
+ },
+ "holdEther": {
+ "message": "Ti permette di tenere ether & token, e serve da ponte per le applicazioni decentralizzate."
+ },
+ "import": {
+ "message": "Importa",
+ "description": "Tasto per importare un account da un file selezionato"
+ },
+ "importAccount": {
+ "message": "Importa Account"
+ },
+ "importAccountMsg": {
+ "message":" Gli account importati non saranno associati alla frase seed originariamente creata con MetaMask. Impara di più sugli account importati "
+ },
+ "importAnAccount": {
+ "message": "Importa un account"
+ },
+ "importDen": {
+ "message": "Importa un DEN Esistente"
+ },
+ "imported": {
+ "message": "Importato",
+ "description": "stato che conferma che un account è stato totalmente caricato nel portachiavi"
+ },
+ "infoHelp": {
+ "message": "Informazioni & Aiuto"
+ },
+ "insufficientFunds": {
+ "message": "Fondi non sufficienti."
+ },
+ "insufficientTokens": {
+ "message": "Token non sufficienti."
+ },
+ "invalidAddress": {
+ "message": "Indirizzo non valido"
+ },
+ "invalidAddressRecipient": {
+ "message": "Indirizzo destinatario invalido"
+ },
+ "invalidGasParams": {
+ "message": "Parametri del Gas non validi"
+ },
+ "invalidInput": {
+ "message": "Input non valido."
+ },
+ "invalidRequest": {
+ "message": "Richiesta non Valida"
+ },
+ "invalidRPC": {
+ "message": "URI RPC invalido"
+ },
+ "jsonFail": {
+ "message": "Qualcosa è andato storto. Assicurati che il file JSON sia formattato correttamente."
+ },
+ "jsonFile": {
+ "message": "File JSON",
+ "description": "formato per importare un account"
+ },
+ "kovan": {
+ "message": "Rete di test Kovan"
+ },
+ "knowledgeDataBase": {
+ "message": "Visita la nostra Knowledge Base"
+ },
+ "lessThanMax": {
+ "message": "deve essere minore o uguale a $1.",
+ "description": "aiuto per inserire un input esadecimale come decimale"
+ },
+ "likeToAddTokens": {
+ "message": "Vorresti aggiungere questi token?"
+ },
+ "limit": {
+ "message": "Limite"
+ },
+ "loading": {
+ "message": "Caricamento..."
+ },
+ "loadingTokens": {
+ "message": "Caricamento Tokens..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "login": {
+ "message": "Connetti"
+ },
+ "logout": {
+ "message": "Disconnetti"
+ },
+ "loose": {
+ "message": "Libero"
+ },
+ "loweCaseWords": {
+ "message": "le frasi seed hanno solo lettere minuscole"
+ },
+ "mainnet": {
+ "message": "Rete Ethereum Principale"
+ },
+ "message": {
+ "message": "Messaggio"
+ },
+ "metamaskDescription": {
+ "message": "MetaMask è una cassaforte sicura per identità su Ethereum."
+ },
+ "min": {
+ "message": "Minimo"
+ },
+ "myAccounts": {
+ "message": "Account Miei"
+ },
+ "mustSelectOne": {
+ "message": "Devi selezionare almeno un token."
+ },
+ "needEtherInWallet": {
+ "message": "Per interagire con applicazioni decentralizzate con MetaMask, devi possedere Ether nel tuo portafoglio."
+ },
+ "needImportFile": {
+ "message": "Devi selezionare un file da importare.",
+ "description": "L'utente sta importando un account e deve aggiungere un file per continuare"
+ },
+ "needImportPassword": {
+ "message": "Dei inserire una password per il file selezionato.",
+ "description": "Password e file necessari per importare un account"
+ },
+ "negativeETH": {
+ "message": "Non puoi inviare una quantità di ETH negativa."
+ },
+ "networks": {
+ "message": "Reti"
+ },
+ "newAccount": {
+ "message": "Nuovo Account"
+ },
+ "newAccountNumberName": {
+ "message": "Account $1",
+ "description": "Nome predefinito per il prossimo account da creare nella schermata di creazione account"
+ },
+ "newContract": {
+ "message": "Nuovo Contratto"
+ },
+ "newPassword": {
+ "message": "Nuova Password (minimo 8 caratteri)"
+ },
+ "newRecipient": {
+ "message": "Nuovo Destinatario"
+ },
+ "newRPC": {
+ "message": "Nuovo URL RPC"
+ },
+ "next": {
+ "message": "Prossimo"
+ },
+ "noAddressForName": {
+ "message": "Nessun indirizzo è stato impostato per questo nome."
+ },
+ "noDeposits": {
+ "message": "Nessun deposito ricevuto"
+ },
+ "noTransactionHistory": {
+ "message": "Nessuna cronologia delle transazioni."
+ },
+ "noTransactions": {
+ "message": "Nessuna Transazione"
+ },
+ "notStarted": {
+ "message": "Non Iniziato"
+ },
+ "oldUI": {
+ "message": "Vecchia interfaccia"
+ },
+ "oldUIMessage": {
+ "message": "Sei ritornato alla vecchia interfaccia. Puoi ritornare alla nuova interfaccia tramite l'opzione nel menu a discesa in alto a destra."
+ },
+ "or": {
+ "message": "o",
+ "description": "scelta tra creare o importare un nuovo account"
+ },
+ "passwordCorrect": {
+ "message": "Assicurati che la password sia corretta."
+ },
+ "passwordMismatch": {
+ "message": "le password non corrispondono",
+ "description": "nella creazione della password, le due password all'interno dei campi non corrispondono"
+ },
+ "passwordShort": {
+ "message": "password non sufficientemente lunga",
+ "description": "nella creazione della password, la password non è lunga abbastanza"
+ },
+ "pastePrivateKey": {
+ "message": "Incolla la tua chiave privata qui:",
+ "description": "Per importare un account da una chiave privata"
+ },
+ "pasteSeed": {
+ "message": "Incolla la tua frase seed qui!"
+ },
+ "personalAddressDetected": {
+ "message": "Rilevato indirizzo personale. Inserisci l'indirizzo del contratto del token."
+ },
+ "pleaseReviewTransaction": {
+ "message": "Ricontrolla la tua transazione."
+ },
+ "privacyMsg": {
+ "message": "Politica sulla Privacy"
+ },
+ "privateKey": {
+ "message": "Chiave Privata",
+ "description": "seleziona questo tipo di file per importare un account"
+ },
+ "privateKeyWarning": {
+ "message": "Attenzione: non dire a nessuno questa chiave! Chiunque con la tua chiave privata può rubare qualsiasi moneta contenuta nel tuo account."
+ },
+ "privateNetwork": {
+ "message": "Rete Privata"
+ },
+ "qrCode": {
+ "message": "Mostra Codice QR"
+ },
+ "readdToken": {
+ "message": "Puoi aggiungere nuovamente questo token in futuro andando in “Aggiungi token” nel menu delle opzioni del tuo account."
+ },
+ "readMore": {
+ "message": "Leggi di più qui."
+ },
+ "readMore2": {
+ "message": "Leggi di più."
+ },
+ "receive": {
+ "message": "Ricevi"
+ },
+ "recipientAddress": {
+ "message": "Indirizzo Destinatario"
+ },
+ "refundAddress": {
+ "message": "Indirizzo di Rimborso"
+ },
+ "rejected": {
+ "message": "Respinta"
+ },
+ "resetAccount": {
+ "message": "Resetta Account"
+ },
+ "restoreFromSeed": {
+ "message": "Ripristina da una frase seed"
+ },
+ "required": {
+ "message": "Richiesto"
+ },
+ "retryWithMoreGas": {
+ "message": "Riprova con un prezzo del Gas maggiore qui"
+ },
+ "revealSeedWords": {
+ "message": "Rivela Frase Seed"
+ },
+ "revealSeedWordsWarning": {
+ "message": "Non ripristinare la tua frase seed in pubblico!. Queste parole possono essere usate per rubare il tuo account."
+ },
+ "revert": {
+ "message": "Annulla"
+ },
+ "rinkeby": {
+ "message": "Rete di test Rinkeby"
+ },
+ "ropsten": {
+ "message": "Rete di test Ropsten"
+ },
+ "sampleAccountName": {
+ "message": "Es: Il mio nuovo account",
+ "description": "Aiuta l'utente a capire il concetto di aggiungere un nome leggibile al loro account"
+ },
+ "save": {
+ "message": "Salva"
+ },
+ "saveAsFile": {
+ "message": "Salva come File",
+ "description": "Processo per esportare un account"
+ },
+ "saveSeedAsFile": {
+ "message": "Salva la Frase Seed come File"
+ },
+ "search": {
+ "message": "Cerca"
+ },
+ "secretPhrase": {
+ "message": "Inserisci la tua frase segreta di dodici parole per ripristinare la cassaforte."
+ },
+ "seedPhraseReq": {
+ "message": "le frasi seed sono lunghe 12 parole"
+ },
+ "select": {
+ "message": "Seleziona"
+ },
+ "selectCurrency": {
+ "message": "Seleziona Moneta"
+ },
+ "selectService": {
+ "message": "Seleziona Servizio"
+ },
+ "selectType": {
+ "message": "Seleziona Tipo"
+ },
+ "send": {
+ "message": "Invia"
+ },
+ "sendETH": {
+ "message": "Invia ETH"
+ },
+ "sendTokens": {
+ "message": "Invia Tokens"
+ },
+ "sendTokensAnywhere": {
+ "message": "Invia Tokens a chiunque abbia un account Ethereum"
+ },
+ "settings": {
+ "message": "Impostazioni"
+ },
+ "shapeshiftBuy": {
+ "message": "Compra con Shapeshift"
+ },
+ "showPrivateKeys": {
+ "message": "Mostra Chiave Privata"
+ },
+ "showQRCode": {
+ "message": "Mostra Codie QR"
+ },
+ "sign": {
+ "message": "Firma"
+ },
+ "signMessage": {
+ "message": "Firma Messaggio"
+ },
+ "signNotice": {
+ "message": "Firmare questo messaggio può avere effetti collaterali pericolosi. \nFirma messaggi da siti di cui ti fidi totalmente. \nQuesto metodo pericoloso sarà rimosso in versioni future."
+ },
+ "sigRequest": {
+ "message": "Firma Richiesta"
+ },
+ "sigRequested": {
+ "message": "Richiesta Firma"
+ },
+ "spaceBetween": {
+ "message": "ci può essere solo uno spazio tra le parole"
+ },
+ "status": {
+ "message": "Stato"
+ },
+ "stateLogs": {
+ "message": "Log di Stato"
+ },
+ "stateLogsDescription": {
+ "message": "I log di stato contengono i tuoi indirizzi pubblici e le transazioni effettuate."
+ },
+ "submit": {
+ "message": "Invia"
+ },
+ "supportCenter": {
+ "message": "Visita il nostro Centro di Supporto"
+ },
+ "symbolBetweenZeroTen": {
+ "message": "Il simbolo deve essere lungo tra 0 e 10 caratteri."
+ },
+ "takesTooLong": {
+ "message": "Ci sta mettendo troppo?"
+ },
+ "terms": {
+ "message": "Termini di Uso"
+ },
+ "testFaucet": {
+ "message": "Prova Faucet"
+ },
+ "to": {
+ "message": "A"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 a ETH via ShapeShift",
+ "description": "il sistema riempirà il tipo di deposito all'inizio del messaggio"
+ },
+ "tokenAddress": {
+ "message": "Indirizzo Token"
+ },
+ "tokenAlreadyAdded": {
+ "message": "Il token è già stato aggiunto."
+ },
+ "tokenBalance": {
+ "message": "Bilancio Token:"
+ },
+ "tokenSelection": {
+ "message": "Cerca un token o seleziona dalla lista di token più popolari."
+ },
+ "tokenSymbol": {
+ "message": "Simbolo Token"
+ },
+ "tokenWarning1": {
+ "message": "Tieni traccia dei token che hai acquistato con il tuo account MetaMask. Se hai acquistato token con un account diverso, quei token non appariranno qui."
+ },
+ "total": {
+ "message": "Totale"
+ },
+ "transactions": {
+ "message": "transazioni"
+ },
+ "transactionMemo": {
+ "message": "Promemoria Transazione (opzionale)"
+ },
+ "transactionNumber": {
+ "message": "Numero Transazione"
+ },
+ "transfers": {
+ "message": "Trasferimenti"
+ },
+ "troubleTokenBalances": {
+ "message": "Abbiamo avuto un problema a caricare il bilancio dei tuoi token. Puoi vederlo ",
+ "description": "Seguito da un link (qui) per vedere il bilancio dei token"
+ },
+ "twelveWords": {
+ "message": "Queste 12 parole sono l'unico modo per ripristinare i tuoi account MetaMask. \nSalvale in un posto sicuro e segreto."
+ },
+ "typePassword": {
+ "message": "Inserisci Password"
+ },
+ "uiWelcome": {
+ "message": "Benvenuto alla nuova interfaccia (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "Stai utilizzanto la nuova interfaccia di MetaMask. Guarda in giro, prova nuove funzionalità come inviare token, e facci sapere se hai dei problemi."
+ },
+ "unavailable": {
+ "message": "Non Disponibile"
+ },
+ "unknown": {
+ "message": "Sconosciuto"
+ },
+ "unknownNetwork": {
+ "message": "Rete Privata Sconosciuta"
+ },
+ "unknownNetworkId": {
+ "message": "ID rete sconosciuto"
+ },
+ "uriErrorMsg": {
+ "message": "Gli URI richiedono un prefisso HTTP/HTTPS."
+ },
+ "usaOnly": {
+ "message": "Solo USA",
+ "description": "Usare questo sito di scambio è possibile solo per persone residenti in USA."
+ },
+ "usedByClients": {
+ "message": "Usato da una varietà di clients diversi"
+ },
+ "useOldUI": {
+ "message": "Use la vecchia UI"
+ },
+ "validFileImport": {
+ "message": "Devi selezionare un file valido da importare."
+ },
+ "vaultCreated": {
+ "message": "Cassaforte Creata"
+ },
+ "viewAccount": {
+ "message": "Vedi Account"
+ },
+ "visitWebSite": {
+ "message": "Visita il nostro sito web"
+ },
+ "warning": {
+ "message": "Attenzione"
+ },
+ "welcomeBeta": {
+ "message": "Benvenuto nella Beta di MetaMask"
+ },
+ "whatsThis": {
+ "message": "Cos'è questo?"
+ },
+ "yourSigRequested": {
+ "message": "E' richiesta la tua firma"
+ },
+ "youSign": {
+ "message": "Ti stai connettendo"
+ }
+} \ No newline at end of file
diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json
index f15d06ff4..bab8d3b95 100644
--- a/app/_locales/ja/messages.json
+++ b/app/_locales/ja/messages.json
@@ -1,10 +1,609 @@
{
+ "accept": {
+ "message": "承認"
+ },
+ "account": {
+ "message": "アカウント"
+ },
+ "accountDetails": {
+ "message": "アカウント詳細"
+ },
+ "accountName": {
+ "message": "アカウント名"
+ },
+ "address": {
+ "message": "アドレス"
+ },
+ "addToken": {
+ "message": "トークンを追加"
+ },
+ "amount": {
+ "message": "金額"
+ },
+ "amountPlusGas": {
+ "message": "金額 + ガス"
+ },
+ "appDescription": {
+ "message": "Ethereumのブラウザ・エクステンション",
+ "description": "The description of the application"
+ },
"appName": {
"message": "MetaMask",
"description": "The name of the application"
},
- "appDescription": {
- "message": "EthereumのID管理",
- "description": "The description of the application"
+ "attemptingConnect": {
+ "message": "ブロックチェーンに接続中"
+ },
+ "available": {
+ "message": "有効"
+ },
+ "back": {
+ "message": "戻る"
+ },
+ "balance": {
+ "message": "残高:"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "現在のガス総量に対して残高が不足しています"
+ },
+ "beta": {
+ "message": "ベータ版"
+ },
+ "betweenMinAndMax": {
+ "message": " $1以上 $2以下にして下さい。",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "borrowDharma": {
+ "message": "Dharmaで借りる(ベータ版)"
+ },
+ "buy": {
+ "message": "購入"
+ },
+ "buyCoinbase": {
+ "message": "Coinbaseで購入"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbaseは、世界で最もポピュラーなBitcoin、Ethereum、そしてLitecoinの取引所です。"
+ },
+ "cancel": {
+ "message": "キャンセル"
+ },
+ "clickCopy": {
+ "message": "クリックしてコピー"
+ },
+ "confirm": {
+ "message": "確認"
+ },
+ "confirmContract": {
+ "message": "コントラクトの確認"
+ },
+ "confirmPassword": {
+ "message": "パスワードの確認"
+ },
+ "confirmTransaction": {
+ "message": "トランザクションの確認"
+ },
+ "continueToCoinbase": {
+ "message": "Coinbaseで続行"
+ },
+ "contractDeployment": {
+ "message": "コントラクトのデプロイ"
+ },
+ "conversionProgress": {
+ "message": "変換中"
+ },
+ "copiedButton": {
+ "message": "コピー完了"
+ },
+ "copiedClipboard": {
+ "message": "クリップボードへコピー済み"
+ },
+ "copiedExclamation": {
+ "message": "コピー完了!"
+ },
+ "copy": {
+ "message": "コピー"
+ },
+ "copyToClipboard": {
+ "message": "クリップボードへコピー"
+ },
+ "copyButton": {
+ "message": " コピー "
+ },
+ "copyPrivateKey": {
+ "message": "これはあなたの秘密鍵です(クリックでコピー)"
+ },
+ "create": {
+ "message": "作成"
+ },
+ "createAccount": {
+ "message": "アカウント作成"
+ },
+ "createDen": {
+ "message": "作成"
+ },
+ "crypto": {
+ "message": "暗号通貨",
+ "description": "Exchange type (cryptocurrencies)"
+ },
+ "customGas": {
+ "message": "ガスのカスタマイズ"
+ },
+ "customize": {
+ "message": "カスタマイズ"
+ },
+ "customRPC": {
+ "message": "カスタムRPC"
+ },
+ "defaultNetwork": {
+ "message": "Etherトランザクションのデフォルトのネットワークはメインネットです。"
+ },
+ "denExplainer": {
+ "message": "DENとは、あなたのパスワードが暗号化されたMetaMask内のストレージです。"
+ },
+ "deposit": {
+ "message": "デポジット"
+ },
+ "depositBTC": {
+ "message": "あなたのBTCを次のアドレスへデポジット:"
+ },
+ "depositCoin": {
+ "message": "あなたの $1を次のアドレスへデポジット",
+ "description": "Tells the user what coin they have selected to deposit with shapeshift"
+ },
+ "depositEth": {
+ "message": "ETHをデポジット"
+ },
+ "depositEther": {
+ "message": "Etherをデポジット"
+ },
+ "depositFiat": {
+ "message": "法定通貨でデポジット"
+ },
+ "depositFromAccount": {
+ "message": "別のアカウントからデポジット"
+ },
+ "depositShapeShift": {
+ "message": "ShapeShiftでデポジット"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "あなたが他の暗号通貨を持っているなら、Etherにトレードしてダイレクトにメタマスクウォレットへのデポジットが可能です。アカウント作成は不要。"
+ },
+ "details": {
+ "message": "詳細"
+ },
+ "directDeposit": {
+ "message": "ダイレクトデポジット"
+ },
+ "directDepositEther": {
+ "message": "Etherをダイレクトデポジット"
+ },
+ "directDepositEtherExplainer": {
+ "message": "あなたがEtherをすでにお持ちなら、ダイレクトデポジットは新しいウォレットにEtherを入手する最も迅速な方法です。"
+ },
+ "done": {
+ "message": "完了"
+ },
+ "edit": {
+ "message": "編集"
+ },
+ "editAccountName": {
+ "message": "アカウント名を編集"
+ },
+ "encryptNewDen": {
+ "message": "新しいDENを暗号化する"
+ },
+ "enterPassword": {
+ "message": "パスワードを入力"
+ },
+ "etherscanView": {
+ "message": "Etherscanでアカウントを見る"
+ },
+ "exchangeRate": {
+ "message": "交換レート"
+ },
+ "exportPrivateKey": {
+ "message": "秘密鍵のエクスポート"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "あなた自身の責任で秘密鍵をエクスポート"
+ },
+ "failed": {
+ "message": "失敗"
+ },
+ "fiat": {
+ "message": "法定通貨",
+ "description": "Exchange type"
+ },
+ "fileImportFail": {
+ "message": "ファイルがインポートされなければ、ここをクリック!",
+ "description": "Helps user import their account from a JSON file"
+ },
+ "from": {
+ "message": "送信元"
+ },
+ "fromShapeShift": {
+ "message": "ShapeShiftから"
+ },
+ "gas": {
+ "message": "ガス",
+ "description": "Short indication of gas cost"
+ },
+ "gasFee": {
+ "message": "ガス料金"
+ },
+ "gasLimit": {
+ "message": "ガスリミット"
+ },
+ "gasLimitCalculation": {
+ "message": "ネットワークの成功率を基にして、ガスリミットを提案しています。"
+ },
+ "gasLimitRequired": {
+ "message": "必要ガスリミット"
+ },
+ "gasLimitTooLow": {
+ "message": "ガスリミットは最低21000です。"
+ },
+ "gasPrice": {
+ "message": "ガスプライス (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "ネットワークの成功率を基にして、ガスプライスを提案しています。"
+ },
+ "gasPriceRequired": {
+ "message": "必要ガスプライス"
+ },
+ "getEther": {
+ "message": "Etherをゲット"
+ },
+ "getEtherFromFaucet": {
+ "message": "フォーセットで $1のEtherをゲット",
+ "description": "Displays network name for Ether faucet"
+ },
+ "greaterThanMin": {
+ "message": " $1以上にして下さい。",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "here": {
+ "message": "ここ",
+ "description": "as in -click here- for more information (goes with troubleTokenBalances)"
+ },
+ "hide": {
+ "message": "隠す"
+ },
+ "hideToken": {
+ "message": "トークンを隠す"
+ },
+ "hideTokenPrompt": {
+ "message": "トークンを隠しますか??"
+ },
+ "howToDeposit": {
+ "message": "どのようにEtherをデポジットしますか?"
+ },
+ "import": {
+ "message": "インポート",
+ "description": "Button to import an account from a selected file"
+ },
+ "importAccount": {
+ "message": "アカウントのインポート"
+ },
+ "importAnAccount": {
+ "message": "アカウントをインポート"
+ },
+ "importDen": {
+ "message": "既存のDENをインポート"
+ },
+ "imported": {
+ "message": "インポート完了",
+ "description": "status showing that an account has been fully loaded into the keyring"
+ },
+ "infoHelp": {
+ "message": "インフォメーションとヘルプ"
+ },
+ "invalidAddress": {
+ "message": "アドレスが無効です。"
+ },
+ "invalidGasParams": {
+ "message": "ガスのパラメーターが無効です。"
+ },
+ "invalidInput": {
+ "message": "インプットが無効です。"
+ },
+ "invalidRequest": {
+ "message": "リクエストが無効です。"
+ },
+ "jsonFile": {
+ "message": "JSONファイル",
+ "description": "format for importing an account"
+ },
+ "kovan": {
+ "message": "Kovanテストネットワーク"
+ },
+ "lessThanMax": {
+ "message": " $1以下にして下さい。",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "limit": {
+ "message": "リミット"
+ },
+ "loading": {
+ "message": "ロード中..."
+ },
+ "loadingTokens": {
+ "message": "トークンをロード中..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "logout": {
+ "message": "ログアウト"
+ },
+ "loose": {
+ "message": "外部秘密鍵"
+ },
+ "mainnet": {
+ "message": "Ethereumメインネットワーク"
+ },
+ "message": {
+ "message": "メッセージ"
+ },
+ "min": {
+ "message": "ミニマム"
+ },
+ "myAccounts": {
+ "message": "マイアカウント"
+ },
+ "needEtherInWallet": {
+ "message": "MetaMaskを使って分散型アプリケーションと対話するためには、あなたのウォレットにEtherが必要になります。"
+ },
+ "needImportFile": {
+ "message": "インポートするファイルを選択してください。",
+ "description": "User is important an account and needs to add a file to continue"
+ },
+ "needImportPassword": {
+ "message": "選択したファイルのパスワードを入力してください。",
+ "description": "Password and file needed to import an account"
+ },
+ "networks": {
+ "message": "ネットワーク"
+ },
+ "newAccount": {
+ "message": "新規アカウント"
+ },
+ "newAccountNumberName": {
+ "message": "アカウント $1",
+ "description": "Default name of next account to be created on create account screen"
+ },
+ "newContract": {
+ "message": "新規コントラクト"
+ },
+ "newPassword": {
+ "message": "新規パスワード(最低8文字)"
+ },
+ "newRecipient": {
+ "message": "新規受取人"
+ },
+ "next": {
+ "message": "次へ"
+ },
+ "noAddressForName": {
+ "message": "この名前にはアドレスが設定されていません。"
+ },
+ "noDeposits": {
+ "message": "デポジットがありません。"
+ },
+ "noTransactionHistory": {
+ "message": "トランザクション履歴がありません。"
+ },
+ "noTransactions": {
+ "message": "トランザクションがありません。"
+ },
+ "notStarted": {
+ "message": "スタートしていません。"
+ },
+ "oldUI": {
+ "message": "旧UI"
+ },
+ "oldUIMessage": {
+ "message": "旧UIを表示しています。右上のドロップダウンメニューのオプションより、新UIへ切り替えが可能です。"
+ },
+ "or": {
+ "message": "または",
+ "description": "choice between creating or importing a new account"
+ },
+ "passwordMismatch": {
+ "message": "パスワードが一致しません。",
+ "description": "in password creation process, the two new password fields did not match"
+ },
+ "passwordShort": {
+ "message": "パスワードが短すぎます。",
+ "description": "in password creation process, the password is not long enough to be secure"
+ },
+ "pastePrivateKey": {
+ "message": "秘密鍵をここにペーストして下さい:",
+ "description": "For importing an account from a private key"
+ },
+ "pasteSeed": {
+ "message": "シードをここにペーストして下さい!"
+ },
+ "pleaseReviewTransaction": {
+ "message": "トランザクションをレビューして下さい。"
+ },
+ "privateKey": {
+ "message": "秘密鍵",
+ "description": "select this type of file to use to import an account"
+ },
+ "privateKeyWarning": {
+ "message": "警告: この鍵は絶対に公開しないで下さい。公開すると、誰でもあなたのアカウント内の資産を盗むことができてしまいます。"
+ },
+ "privateNetwork": {
+ "message": "プライベート・ネットワーク"
+ },
+ "qrCode": {
+ "message": "QRコードを表示"
+ },
+ "readdToken": {
+ "message": "アカウントのオプションメニューから「トークンを追加」すれば、将来このトークンを追加し直すことができます。"
+ },
+ "readMore": {
+ "message": "もっと読む"
+ },
+ "receive": {
+ "message": "受け取る"
+ },
+ "recipientAddress": {
+ "message": "受取人アドレス"
+ },
+ "refundAddress": {
+ "message": "あなたの返金先アドレス"
+ },
+ "rejected": {
+ "message": "拒否されました"
+ },
+ "required": {
+ "message": "必要です。"
+ },
+ "retryWithMoreGas": {
+ "message": "より高いガスプライスで再度試して下さい。"
+ },
+ "revert": {
+ "message": "元に戻す"
+ },
+ "rinkeby": {
+ "message": "Rinkebyテストネットワーク"
+ },
+ "ropsten": {
+ "message": "Ropstenテストネットワーク"
+ },
+ "sampleAccountName": {
+ "message": "例.新しいマイアカウント",
+ "description": "Help user understand concept of adding a human-readable name to their account"
+ },
+ "save": {
+ "message": "保存"
+ },
+ "saveAsFile": {
+ "message": "ファイルとして保存",
+ "description": "Account export process"
+ },
+ "selectService": {
+ "message": "サービスを選択"
+ },
+ "send": {
+ "message": "送信"
+ },
+ "sendTokens": {
+ "message": "トークンを送る"
+ },
+ "sendTokensAnywhere": {
+ "message": "イーサリアムのアカウントを持っている人にトークンを送る"
+ },
+ "settings": {
+ "message": "設定"
+ },
+ "shapeshiftBuy": {
+ "message": "Shapeshiftで買う"
+ },
+ "showPrivateKeys": {
+ "message": "秘密鍵を表示"
+ },
+ "showQRCode": {
+ "message": "QRコードを表示"
+ },
+ "sign": {
+ "message": "署名"
+ },
+ "signMessage": {
+ "message": "メッセージに署名"
+ },
+ "signNotice": {
+ "message": "このメッセージへの署名は危険となる可能性があります。\n完全に信頼するサイトからのメッセージのみ、\nあなたのアカウントで署名して下さい。今後のバージョンでは、\nこの危険なメソッドは削除される予定です。"
+ },
+ "sigRequest": {
+ "message": "署名リクエスト"
+ },
+ "sigRequested": {
+ "message": "署名がリクエストされました"
+ },
+ "status": {
+ "message": "ステータス"
+ },
+ "submit": {
+ "message": "送信"
+ },
+ "takesTooLong": {
+ "message": "長くかかりすぎていますか?"
+ },
+ "testFaucet": {
+ "message": "Faucetをテスト"
+ },
+ "to": {
+ "message": "宛先"
+ },
+ "toETHviaShapeShift": {
+ "message": "ShapeShiftで $1をETHにする",
+ "description": "system will fill in deposit type in start of message"
+ },
+ "tokenBalance": {
+ "message": "あなたのトークン残高:"
+ },
+ "total": {
+ "message": "合計"
+ },
+ "transactionMemo": {
+ "message": "トランザクションメモ (オプション)"
+ },
+ "transactionNumber": {
+ "message": "トランザクション番号"
+ },
+ "transfers": {
+ "message": "トランスファー"
+ },
+ "troubleTokenBalances": {
+ "message": "トークン残高を取得できません。こちらでご確認ください。",
+ "description": "Followed by a link (here) to view token balances"
+ },
+ "typePassword": {
+ "message": "パスワードタイプ"
+ },
+ "uiWelcome": {
+ "message": "新UIへようこそ!(ベータ版)"
+ },
+ "uiWelcomeMessage": {
+ "message": "現在Metamaskの新しいUIをお使いになっています。トークン送信など、新たな機能を試してみましょう!何か問題があればご報告ください。"
+ },
+ "unavailable": {
+ "message": "有効ではありません。"
+ },
+ "unknown": {
+ "message": "不明"
+ },
+ "unknownNetwork": {
+ "message": "不明なプライベートネットワーク"
+ },
+ "unknownNetworkId": {
+ "message": "不明なネットワークID"
+ },
+ "usaOnly": {
+ "message": "米国居住者のみ",
+ "description": "Using this exchange is limited to people inside the USA"
+ },
+ "usedByClients": {
+ "message": "様々なクライアントによって使用されています。"
+ },
+ "viewAccount": {
+ "message": "アカウントを見る"
+ },
+ "warning": {
+ "message": "警告"
+ },
+ "whatsThis": {
+ "message": "これは何でしょう?"
+ },
+ "yourSigRequested": {
+ "message": "あなたの署名がリクエストされています。"
+ },
+ "youSign": {
+ "message": "署名しています。"
}
}
diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json
index c58af4b80..d3801c4f5 100644
--- a/app/_locales/ko/messages.json
+++ b/app/_locales/ko/messages.json
@@ -1,10 +1,609 @@
{
- "appName": {
- "message": "MetaMask",
- "description": "The name of the application"
+ "accept": {
+ "message": "수락"
+ },
+ "account": {
+ "message": "계좌"
+ },
+ "accountDetails": {
+ "message": "계좌 상세보기"
+ },
+ "accountName": {
+ "message": "계좌 이름"
+ },
+ "address": {
+ "message": "주소"
+ },
+ "addToken": {
+ "message": "토큰 추가"
+ },
+ "amount": {
+ "message": "금액"
+ },
+ "amountPlusGas": {
+ "message": "금액 + 가스"
},
"appDescription": {
- "message": "이더리움 계좌 관리",
- "description": "The description of the application"
+ "message": "이더리움 브라우저 확장 프로그램",
+ "description": "어플리케이션 내용"
+ },
+ "appName": {
+ "message": "메타마스크",
+ "description": "어플리케이션 이름"
+ },
+ "attemptingConnect": {
+ "message": "블록체인에 접속 시도 중입니다."
+ },
+ "available": {
+ "message": "사용 가능한"
+ },
+ "back": {
+ "message": "뒤로"
+ },
+ "balance": {
+ "message": "잔액:"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "가스가 충분하지 않습니다."
+ },
+ "beta": {
+ "message": "베타"
+ },
+ "betweenMinAndMax": {
+ "message": "$1 이상 $2 이하여야 합니다.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "borrowDharma": {
+ "message": "Dharma에서 빌리기(베타)"
+ },
+ "buy": {
+ "message": "구매"
+ },
+ "buyCoinbase": {
+ "message": "코인베이스에서 구매"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "코인베이스에서 비트코인, 이더리움, 라이트코인을 구매하실 수 있습니다."
+ },
+ "cancel": {
+ "message": "취소"
+ },
+ "clickCopy": {
+ "message": "클릭하여 복사"
+ },
+ "confirm": {
+ "message": "승인"
+ },
+ "confirmContract": {
+ "message": "컨트랙트 승인"
+ },
+ "confirmPassword": {
+ "message": "패스워드 승인"
+ },
+ "confirmTransaction": {
+ "message": "트랜잭션 승인"
+ },
+ "continueToCoinbase": {
+ "message": "코인베이스로 계속하기"
+ },
+ "contractDeployment": {
+ "message": "컨트랙트 배포"
+ },
+ "conversionProgress": {
+ "message": "변환중.."
+ },
+ "copiedButton": {
+ "message": "복사되었습니다."
+ },
+ "copiedClipboard": {
+ "message": "클립보드에 복사되었습니다."
+ },
+ "copiedExclamation": {
+ "message": "복사되었습니다."
+ },
+ "copy": {
+ "message": "복사하기"
+ },
+ "copyToClipboard": {
+ "message": "클립보드에 복사"
+ },
+ "copyButton": {
+ "message": " 복사 "
+ },
+ "copyPrivateKey": {
+ "message": "비밀 키 (클릭하여 복사)"
+ },
+ "create": {
+ "message": "생성"
+ },
+ "createAccount": {
+ "message": "계좌 생성"
+ },
+ "createDen": {
+ "message": "생성"
+ },
+ "crypto": {
+ "message": "암호화폐",
+ "description": "Exchange type (cryptocurrencies)"
+ },
+ "customGas": {
+ "message": "가스 설정"
+ },
+ "customize": {
+ "message": "커스터마이즈"
+ },
+ "customRPC": {
+ "message": "커스텀 RPC"
+ },
+ "defaultNetwork": {
+ "message": "이더리움 트랜잭션의 기본 네트워크는 메인넷입니다."
+ },
+ "denExplainer": {
+ "message": "DEN은 비밀번호가 암호화 된 MetaMask의 스토리지입니다."
+ },
+ "deposit": {
+ "message": "입금"
+ },
+ "depositBTC": {
+ "message": "아래 주소로 BTC를 입급해주세요."
+ },
+ "depositCoin": {
+ "message": "아래 주소로 $1를 입금해주세요.",
+ "description": "Tells the user what coin they have selected to deposit with shapeshift"
+ },
+ "depositEth": {
+ "message": "이더 입금"
+ },
+ "depositEther": {
+ "message": "이더 입금"
+ },
+ "depositFiat": {
+ "message": "현금으로 입금하기"
+ },
+ "depositFromAccount": {
+ "message": "다른 주소에서 입금하기"
+ },
+ "depositShapeShift": {
+ "message": "ShapeShift를 통해 입금하기"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "다른 암호화폐를 가지고 있으면, 계좌 생성 필요없이, 거래를 하거나 메타마스크 지갑을 통해 이더를 입금할 수 있습니다."
+ },
+ "details": {
+ "message": "상세"
+ },
+ "directDeposit": {
+ "message": "즉시 입금"
+ },
+ "directDepositEther": {
+ "message": "이더 즉시 입금"
+ },
+ "directDepositEtherExplainer": {
+ "message": "이더를 이미 보유하고 있다면, 직접 입금을 통해 이더를 즉시 입금하실 수 있습니다."
+ },
+ "done": {
+ "message": "완료"
+ },
+ "edit": {
+ "message": "수정"
+ },
+ "editAccountName": {
+ "message": "계좌명 수정"
+ },
+ "encryptNewDen": {
+ "message": "새 DEN 암호화"
+ },
+ "enterPassword": {
+ "message": "패스워드를 입력해주세요."
+ },
+ "etherscanView": {
+ "message": "이더스캔에서 계좌보기"
+ },
+ "exchangeRate": {
+ "message": "환율"
+ },
+ "exportPrivateKey": {
+ "message": "비밀키 내보내기"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "Export private keys at your own risk."
+ },
+ "failed": {
+ "message": "실패"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Exchange type"
+ },
+ "fileImportFail": {
+ "message": "파일을 가져올 수 없나요? 여기를 클릭해주세요!",
+ "description": "Helps user import their account from a JSON file"
+ },
+ "from": {
+ "message": "보내는 사람"
+ },
+ "fromShapeShift": {
+ "message": "ShapeShift로 부터"
+ },
+ "gas": {
+ "message": "가스",
+ "description": "Short indication of gas cost"
+ },
+ "gasFee": {
+ "message": "가스 수수료"
+ },
+ "gasLimit": {
+ "message": "가스 리밋"
+ },
+ "gasLimitCalculation": {
+ "message": "네트워크 성공률을 기반으로 적합한 가스 리밋을 계산합니다."
+ },
+ "gasLimitRequired": {
+ "message": "가스 리밋이 필요합니다."
+ },
+ "gasLimitTooLow": {
+ "message": "가스 리밋은 21000 이상이여야 합니다."
+ },
+ "gasPrice": {
+ "message": "가스 가격 (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "네트워크 성공률을 기반으로 적합한 가스 가격을 계산합니다."
+ },
+ "gasPriceRequired": {
+ "message": "가스 가격이 필요합니다."
+ },
+ "getEther": {
+ "message": "이더 얻기"
+ },
+ "getEtherFromFaucet": {
+ "message": "faucet에서 $1에 달하는 이더를 얻으세요.",
+ "description": "Displays network name for Ether faucet"
+ },
+ "greaterThanMin": {
+ "message": "$1 이상이어야 합니다.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "here": {
+ "message": "여기",
+ "description": "as in -click here- for more information (goes with troubleTokenBalances)"
+ },
+ "hide": {
+ "message": "숨기기"
+ },
+ "hideToken": {
+ "message": "토큰 숨기기"
+ },
+ "hideTokenPrompt": {
+ "message": "토큰 숨기기?"
+ },
+ "howToDeposit": {
+ "message": "어떤 방법으로 이더를 입금하시겠습니까?"
+ },
+ "import": {
+ "message": "파일에서 가져오기",
+ "description": "Button to import an account from a selected file"
+ },
+ "importAccount": {
+ "message": "계좌 가져오기"
+ },
+ "importAnAccount": {
+ "message": "계좌 가져오기"
+ },
+ "importDen": {
+ "message": "기존 DEN 가져오기"
+ },
+ "imported": {
+ "message": "가져오기 완료",
+ "description": "status showing that an account has been fully loaded into the keyring"
+ },
+ "infoHelp": {
+ "message": "정보 및 도움말"
+ },
+ "invalidAddress": {
+ "message": "유효하지 않은 주소"
+ },
+ "invalidGasParams": {
+ "message": "유효하지 않은 가스 입력값"
+ },
+ "invalidInput": {
+ "message": "유효하지 않은 입력값"
+ },
+ "invalidRequest": {
+ "message": "유효하지 않은 요청"
+ },
+ "jsonFile": {
+ "message": "JSON 파일",
+ "description": "format for importing an account"
+ },
+ "kovan": {
+ "message": "Kovan 테스트넷"
+ },
+ "lessThanMax": {
+ "message": "$1 이하여야합니다.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "limit": {
+ "message": "리밋"
+ },
+ "loading": {
+ "message": "로딩중..."
+ },
+ "loadingTokens": {
+ "message": "토큰 로딩중..."
+ },
+ "localhost": {
+ "message": "로컬호스트 8545"
+ },
+ "logout": {
+ "message": "로그아웃"
+ },
+ "loose": {
+ "message": "외부 비밀키"
+ },
+ "mainnet": {
+ "message": "이더리움 메인넷"
+ },
+ "message": {
+ "message": "메시지"
+ },
+ "min": {
+ "message": "최소"
+ },
+ "myAccounts": {
+ "message": "내 계좌"
+ },
+ "needEtherInWallet": {
+ "message": "dApp을 이용하기 위해서는 지갑에 이더가 있어야 합니다."
+ },
+ "needImportFile": {
+ "message": "가져올 파일을 선택해주세요.",
+ "description": "User is important an account and needs to add a file to continue"
+ },
+ "needImportPassword": {
+ "message": "선택 된 파일에 패스워드를 입력해주세요.",
+ "description": "Password and file needed to import an account"
+ },
+ "networks": {
+ "message": "네트워크"
+ },
+ "newAccount": {
+ "message": "새 계좌"
+ },
+ "newAccountNumberName": {
+ "message": "새 계좌 $1",
+ "description": "Default name of next account to be created on create account screen"
+ },
+ "newContract": {
+ "message": "새 컨트랙트"
+ },
+ "newPassword": {
+ "message": "새 패스워드 (최소 8자 이상)"
+ },
+ "newRecipient": {
+ "message": "받는 사람"
+ },
+ "next": {
+ "message": "다음"
+ },
+ "noAddressForName": {
+ "message": "이 이름에는 주소가 설정되어 있지 않습니다."
+ },
+ "noDeposits": {
+ "message": "입금이 없습니다."
+ },
+ "noTransactionHistory": {
+ "message": "트랜잭션 기록이 없습니다."
+ },
+ "noTransactions": {
+ "message": "트랜잭션이 없습니다."
+ },
+ "notStarted": {
+ "message": "시작되지 않음."
+ },
+ "oldUI": {
+ "message": "구버전의 UI"
+ },
+ "oldUIMessage": {
+ "message": "구버전 UI로 변경하셨습니다. 우 상단 드랍다운 메뉴에서 새 UI로 변경하실 수 있습니다."
+ },
+ "or": {
+ "message": "또는",
+ "description": "choice between creating or importing a new account"
+ },
+ "passwordMismatch": {
+ "message": "패스워드가 일치하지 않습니다.",
+ "description": "in password creation process, the two new password fields did not match"
+ },
+ "passwordShort": {
+ "message": "패스워드가 너무 짧습니다.",
+ "description": "in password creation process, the password is not long enough to be secure"
+ },
+ "pastePrivateKey": {
+ "message": "비밀키를 입력해주세요.",
+ "description": "For importing an account from a private key"
+ },
+ "pasteSeed": {
+ "message": "시드 문장들을 붙여넣어주세요."
+ },
+ "pleaseReviewTransaction": {
+ "message": "트랜잭션을 검토해주세요."
+ },
+ "privateKey": {
+ "message": "비밀키",
+ "description": "select this type of file to use to import an account"
+ },
+ "privateKeyWarning": {
+ "message": " 절대 이 키를 노출하지 마십시오. 비밀키가 노출되면 누구나 당신의 계좌에서 자산을 빼갈 수 있습니다."
+ },
+ "privateNetwork": {
+ "message": "프라이빗 네트워크"
+ },
+ "qrCode": {
+ "message": "QR 코드 보기"
+ },
+ "readdToken": {
+ "message": "옵션 메뉴에서 “토큰 추가”를 눌러서 추후에 다시 이 토큰을 추가하실 수 있습니다."
+ },
+ "readMore": {
+ "message": "더 읽기."
+ },
+ "receive": {
+ "message": "받기"
+ },
+ "recipientAddress": {
+ "message": "받는 사람 주소"
+ },
+ "refundAddress": {
+ "message": "환불받을 주소"
+ },
+ "rejected": {
+ "message": "거부되었음."
+ },
+ "required": {
+ "message": "필요함."
+ },
+ "retryWithMoreGas": {
+ "message": "더 높은 가스 가격으로 다시 시도해주세요."
+ },
+ "revert": {
+ "message": "취소"
+ },
+ "rinkeby": {
+ "message": "Rinkeby 테스트넷"
+ },
+ "ropsten": {
+ "message": "Ropsten 테스트넷"
+ },
+ "sampleAccountName": {
+ "message": "예) 나의 새 계좌",
+ "description": "Help user understand concept of adding a human-readable name to their account"
+ },
+ "save": {
+ "message": "저장"
+ },
+ "saveAsFile": {
+ "message": "파일로 저장",
+ "description": "Account export process"
+ },
+ "selectService": {
+ "message": "서비스 선택"
+ },
+ "send": {
+ "message": "전송"
+ },
+ "sendTokens": {
+ "message": "토큰 전송"
+ },
+ "sendTokensAnywhere": {
+ "message": "이더 계좌로 토큰 전송"
+ },
+ "settings": {
+ "message": "설정"
+ },
+ "shapeshiftBuy": {
+ "message": "Shapeshift를 통해서 구매하기"
+ },
+ "showPrivateKeys": {
+ "message": "비밀키 보기"
+ },
+ "showQRCode": {
+ "message": "QR코드 보기"
+ },
+ "sign": {
+ "message": "서명"
+ },
+ "signMessage": {
+ "message": "서명 메시지"
+ },
+ "signNotice": {
+ "message": "이 메시지에 대한 서명은 위험할 수 있습니다.\n 완전히 신뢰할 수 있는 사이트에서만 서명해주세요.\n 안전을 위해 추후의 버전에서는 삭제될 기능입니다. "
+ },
+ "sigRequest": {
+ "message": "서명 요청"
+ },
+ "sigRequested": {
+ "message": "서명이 요청되었습니다."
+ },
+ "status": {
+ "message": "상태"
+ },
+ "submit": {
+ "message": "제출"
+ },
+ "takesTooLong": {
+ "message": "너무 오래걸리나요?"
+ },
+ "testFaucet": {
+ "message": "Faucet 테스트"
+ },
+ "to": {
+ "message": "대상"
+ },
+ "toETHviaShapeShift": {
+ "message": "ShapeShift를 통해 $1를 ETH로 바꾸기",
+ "description": "system will fill in deposit type in start of message"
+ },
+ "tokenBalance": {
+ "message": "현재 토큰 잔액: "
+ },
+ "total": {
+ "message": "합계"
+ },
+ "transactionMemo": {
+ "message": "트랜잭션 메모 (선택사항)"
+ },
+ "transactionNumber": {
+ "message": "트랜잭션 번호"
+ },
+ "transfers": {
+ "message": "전송"
+ },
+ "troubleTokenBalances": {
+ "message": "토큰 잔액을 가져오는데에 문제가 생겼습니다. (여기)서 상세내용을 볼 수 있습니다.",
+ "description": "Followed by a link (here) to view token balances"
+ },
+ "typePassword": {
+ "message": "패스워드를 입력하세요."
+ },
+ "uiWelcome": {
+ "message": "새 UI에 오신 것을 환영합니다. (베타)"
+ },
+ "uiWelcomeMessage": {
+ "message": "새 메타마스크 UI를 사용하고 계십니다. 토큰 전송과 같은 새 기능들을 사용해보시면서 문제가 있다면 알려주세요."
+ },
+ "unavailable": {
+ "message": "유효하지 않은"
+ },
+ "unknown": {
+ "message": "알려지지 않은"
+ },
+ "unknownNetwork": {
+ "message": "알려지지 않은 프라이빗 네트워크"
+ },
+ "unknownNetworkId": {
+ "message": "알려지지 않은 네트워크 ID"
+ },
+ "usaOnly": {
+ "message": "USA 거주자 한정",
+ "description": "Using this exchange is limited to people inside the USA"
+ },
+ "usedByClients": {
+ "message": "다양한 클라이언트에서 사용되고 있습니다."
+ },
+ "viewAccount": {
+ "message": "계좌 보기"
+ },
+ "warning": {
+ "message": "경고"
+ },
+ "whatsThis": {
+ "message": "이것은 무엇인가요?"
+ },
+ "yourSigRequested": {
+ "message": "서명이 요청되고 있습니다."
+ },
+ "youSign": {
+ "message": "서명 중입니다."
}
}
diff --git a/app/_locales/ph/messages.json b/app/_locales/ph/messages.json
new file mode 100644
index 000000000..29d63be02
--- /dev/null
+++ b/app/_locales/ph/messages.json
@@ -0,0 +1,609 @@
+{
+ "accept": {
+ "message": "Tanggapin"
+ },
+ "account": {
+ "message": "Account"
+ },
+ "accountDetails": {
+ "message": "Detalye ng Account"
+ },
+ "accountName": {
+ "message": "Pangalan ng Account"
+ },
+ "address": {
+ "message": "Address"
+ },
+ "addToken": {
+ "message": "Magdagdag ng Token"
+ },
+ "amount": {
+ "message": "Halaga"
+ },
+ "amountPlusGas": {
+ "message": "Halaga + Gas"
+ },
+ "appDescription": {
+ "message": "Ethereum Browser Extension",
+ "description": "Ang deskripsyon ng application"
+ },
+ "appName": {
+ "message": "MetaMask",
+ "description": "Ang pangalan ng application"
+ },
+ "attemptingConnect": {
+ "message": "Sinusubukang kumonekta sa blockchain."
+ },
+ "available": {
+ "message": "Magagamit"
+ },
+ "back": {
+ "message": "Bumalik"
+ },
+ "balance": {
+ "message": "Balanse:"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Kulang ang balanse para sa kasalukuyang gas total"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "dapat mas malaki o katumbas ng $1 at mas mababa o katumbas ng $2.",
+ "description": "helper para sa pag-input ng hex bilang decimal input"
+ },
+ "borrowDharma": {
+ "message": "Humiram sa Dharma (Beta)"
+ },
+ "buy": {
+ "message": "Bumili"
+ },
+ "buyCoinbase": {
+ "message": "Bumili sa Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Ang Coinbase ang pinakasikat na paraan upang bumili at magbenta ng bitcoin, ethereum, at litecoin sa buong mundo."
+ },
+ "cancel": {
+ "message": "Kanselahin"
+ },
+ "clickCopy": {
+ "message": "I-click upang Makopya"
+ },
+ "confirm": {
+ "message": "Tiyakin"
+ },
+ "confirmContract": {
+ "message": "Tiyakin ang Contract"
+ },
+ "confirmPassword": {
+ "message": "Tiyakin ang Password"
+ },
+ "confirmTransaction": {
+ "message": "Tiyakin ang Transaksyon"
+ },
+ "continueToCoinbase": {
+ "message": "Magpatuloy sa Coinbase"
+ },
+ "contractDeployment": {
+ "message": "Pag-deploy ng Contract"
+ },
+ "conversionProgress": {
+ "message": "Isinasagawa ang conversion"
+ },
+ "copiedButton": {
+ "message": "Kinopya"
+ },
+ "copiedClipboard": {
+ "message": "Kinopya sa Clipboard"
+ },
+ "copiedExclamation": {
+ "message": "Kinopya!"
+ },
+ "copy": {
+ "message": "Kinopya"
+ },
+ "copyToClipboard": {
+ "message": "Kinopya sa clipboard"
+ },
+ "copyButton": {
+ "message": " Kinopya "
+ },
+ "copyPrivateKey": {
+ "message": "Ito ang iyong private key (i-click upang makopya)"
+ },
+ "create": {
+ "message": "Gumawa"
+ },
+ "createAccount": {
+ "message": "Gumawa ng Account"
+ },
+ "createDen": {
+ "message": "Gumawa"
+ },
+ "crypto": {
+ "message": "Crypto",
+ "description": "Type ng exchange (cryptocurrencies)"
+ },
+ "customGas": {
+ "message": "I-customize ang Gas"
+ },
+ "customize": {
+ "message": "I-customize"
+ },
+ "customRPC": {
+ "message": "Custom RPC"
+ },
+ "defaultNetwork": {
+ "message": "Ang default network para sa Ether transactions ay ang Main Net."
+ },
+ "denExplainer": {
+ "message": "Ang iyong DEN ang nagsisilbing password-encrypted storage mo sa loob ng MetaMask."
+ },
+ "deposit": {
+ "message": "Deposito"
+ },
+ "depositBTC": {
+ "message": "I-deposito ang iyong BTC sa address na ito:"
+ },
+ "depositCoin": {
+ "message": "I-deposito ang iyong $1 sa address na ito",
+ "description": "Sinasabihan ang user kung ano ang coin na kanilang pinili para I-deposito gamit ang shapeshift"
+ },
+ "depositEth": {
+ "message": "I-deposito ang Eth"
+ },
+ "depositEther": {
+ "message": "I-deposito ang Ether"
+ },
+ "depositFiat": {
+ "message": "I-deposito ang Fiat"
+ },
+ "depositFromAccount": {
+ "message": "I-deposito mula sa ibang account"
+ },
+ "depositShapeShift": {
+ "message": "I-deposito gamit ang ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "Kung ikaw ay nagmamay-ari ng iba pang cryptocurrencies, pwede kang mag-trade at mag-deposito ng Ether diretso sa iyong MetaMask wallet. Hindi mo na kailangan ng account."
+ },
+ "details": {
+ "message": "Detalye"
+ },
+ "directDeposit": {
+ "message": "Direktang Deposito"
+ },
+ "directDepositEther": {
+ "message": "Direktang I-deposito ang Ether"
+ },
+ "directDepositEtherExplainer": {
+ "message": "Kung ika ay mayroon nang Ether, ang pinakamabilis na paraan upang makuha ang Ether sa iyong bagong wallet ay sa pamamagitan ng direktang deposito."
+ },
+ "done": {
+ "message": "Tapos na"
+ },
+ "edit": {
+ "message": "I-edit"
+ },
+ "editAccountName": {
+ "message": "I-edit ang Pangalang ng Account"
+ },
+ "encryptNewDen": {
+ "message": "I-encrypt ang iyong bagong DEN"
+ },
+ "enterPassword": {
+ "message": "I-enter ang password"
+ },
+ "etherscanView": {
+ "message": "Tingnan ang account sa Etherscan"
+ },
+ "exchangeRate": {
+ "message": "Exchange Rate"
+ },
+ "exportPrivateKey": {
+ "message": "I-export ang Private Key"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "I-export ang private keys at intindihin ang panganib na kasama nito."
+ },
+ "failed": {
+ "message": "Nabigo"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Type ng exchange"
+ },
+ "fileImportFail": {
+ "message": "Hindi gumagana ang file import? I-click ito!",
+ "description": "Tinutulungan ang user na i-import ang kanilang account mula sa JSON file"
+ },
+ "from": {
+ "message": "Mula sa"
+ },
+ "fromShapeShift": {
+ "message": "Mula sa ShapeShift"
+ },
+ "gas": {
+ "message": "Gas",
+ "description": "Maikling indikasyon ng gas cost"
+ },
+ "gasFee": {
+ "message": "Gas Fee"
+ },
+ "gasLimit": {
+ "message": "Gas Limit"
+ },
+ "gasLimitCalculation": {
+ "message": "Kinalkula namin ang iminungkahing gas limit base sa network success rates."
+ },
+ "gasLimitRequired": {
+ "message": "Kailangan ang Gas Limit"
+ },
+ "gasLimitTooLow": {
+ "message": "Ang gas limit ay hindi dabat bababa sa 21000"
+ },
+ "gasPrice": {
+ "message": "Gas Price (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "Kinalkula namin ang iminungkahing gas prices base sa network success rates."
+ },
+ "gasPriceRequired": {
+ "message": "Kailangan ang Gas Price"
+ },
+ "getEther": {
+ "message": "Kumuha ng Ether"
+ },
+ "getEtherFromFaucet": {
+ "message": "Kumuha ng Ether mula sa faucet para sa $1",
+ "description": "Ipinapakita ang pangalan ng network para sa Ether faucet"
+ },
+ "greaterThanMin": {
+ "message": "dapat mas malaki o katumbas ng $1.",
+ "description": "helper para sa pag-input ng hex bilang decimal input"
+ },
+ "here": {
+ "message": "i-click ito",
+ "description": "tulad ng -i-click dito- para sa mas maraming impormasyon (kasama ng troubleTokenBalances)"
+ },
+ "hide": {
+ "message": "Itago"
+ },
+ "hideToken": {
+ "message": "Itago ang Token"
+ },
+ "hideTokenPrompt": {
+ "message": "Itago ang Token?"
+ },
+ "howToDeposit": {
+ "message": "Paano mo gustong mag-deposito ng Ether?"
+ },
+ "import": {
+ "message": "I-import",
+ "description": "Button para i-import ang account mula sa napiling file"
+ },
+ "importAccount": {
+ "message": "I-import ang Account"
+ },
+ "importAnAccount": {
+ "message": "I-import ang account"
+ },
+ "importDen": {
+ "message": "I-import ang Existing DEN"
+ },
+ "imported": {
+ "message": "Na-import na",
+ "description": "status na nagpapakita na ang account ay lubos na na-load sa keyring"
+ },
+ "infoHelp": {
+ "message": "Impormasyon at Tulong"
+ },
+ "invalidAddress": {
+ "message": "Invalid ang address"
+ },
+ "invalidGasParams": {
+ "message": "Invalid ang Gas Parameters"
+ },
+ "invalidInput": {
+ "message": "Invalid ang input."
+ },
+ "invalidRequest": {
+ "message": "Invalid ang Request"
+ },
+ "jsonFile": {
+ "message": "JSON File",
+ "description": "format para sa pag-import ng account"
+ },
+ "kovan": {
+ "message": "Kovan Test Network"
+ },
+ "lessThanMax": {
+ "message": "dapat mas mababa o katumbas ng $1.",
+ "description": "helper para sa pag-input ng hex bilang decimal input"
+ },
+ "limit": {
+ "message": "Limitasyon"
+ },
+ "loading": {
+ "message": "Naglo-load..."
+ },
+ "loadingTokens": {
+ "message": "Naglo-load ang Tokens..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "logout": {
+ "message": "Log out"
+ },
+ "loose": {
+ "message": "Loose"
+ },
+ "mainnet": {
+ "message": "Main Ethereum Network"
+ },
+ "message": {
+ "message": "Mensahe"
+ },
+ "min": {
+ "message": "Minimum"
+ },
+ "myAccounts": {
+ "message": "Aking mga Account"
+ },
+ "needEtherInWallet": {
+ "message": "Upang makipag-ugnayan sa decentralized applications gamit ang MetaMask, kakailanganin mo ng Ether sa iyong wallet."
+ },
+ "needImportFile": {
+ "message": "Dapat kang pumili ng file para i-import.",
+ "description": "Ang user ay nag-iimport ng account at kailangan magdagdag ng file upang tumuloy"
+ },
+ "needImportPassword": {
+ "message": "Dapat mong i-enter ang password para sa napiling file.",
+ "description": "Password at file na kailangan upang ma-import ang account"
+ },
+ "networks": {
+ "message": "Networks"
+ },
+ "newAccount": {
+ "message": "Bagong Account"
+ },
+ "newAccountNumberName": {
+ "message": "Account $1",
+ "description": "Ang default na pangalan ng susunod na account na gagawin sa create account screen"
+ },
+ "newContract": {
+ "message": "Bagong Contract"
+ },
+ "newPassword": {
+ "message": "Bagong Password (min 8 chars)"
+ },
+ "newRecipient": {
+ "message": "Bagong Recipient"
+ },
+ "next": {
+ "message": "Sunod"
+ },
+ "noAddressForName": {
+ "message": "Walang naka-set na address para sa pangalang ito."
+ },
+ "noDeposits": {
+ "message": "Walang natanggap na mga deposito"
+ },
+ "noTransactionHistory": {
+ "message": "Walang kasaysayan ng transaksyon."
+ },
+ "noTransactions": {
+ "message": "Walang mga Transaksyon"
+ },
+ "notStarted": {
+ "message": "Hindi Sinimulan"
+ },
+ "oldUI": {
+ "message": "Lumang UI"
+ },
+ "oldUIMessage": {
+ "message": "Ikaw ay bumalik sa lumang UI. Maaari kang bumalik sa bagong UI mula sa isang opsyon sa dropdown menu na matatagpuan sa bandang taas at kanan."
+ },
+ "or": {
+ "message": "o",
+ "description": "Pagpili sa pagitan ng paggawa of pag-import ng bagong account"
+ },
+ "passwordMismatch": {
+ "message": "hindi nagtugma ang mga password",
+ "description": "Sa proseso ng paggawa ng password, ang dalawang password fields ay hindi nagtugma"
+ },
+ "passwordShort": {
+ "message": "hindi sapat ang haba ng password",
+ "description": "Sa proseso ng paggawa ng password, ang password ay hindi in password creation process, hind sapat ang haba ng password upang maging ligtas"
+ },
+ "pastePrivateKey": {
+ "message": "I-paste dito ang iyong private key string:",
+ "description": "Para sa pag-import ng account mula sa private key"
+ },
+ "pasteSeed": {
+ "message": "I-paste dito ang iyong seed phrase!"
+ },
+ "pleaseReviewTransaction": {
+ "message": "Mangyaring suriin ang iyong transaksyon."
+ },
+ "privateKey": {
+ "message": "Private Key",
+ "description": "Piliin ang ganitong type ng file upang gamitin sa pag-import ng account"
+ },
+ "privateKeyWarning": {
+ "message": "Babala: Huwag sabihin sa kahit na sino ang key na ito. Maaring makuha at manakaw ng sinumang nakakaalam ng iyong private key ang mga assets sa iyong account."
+ },
+ "privateNetwork": {
+ "message": "Pribadong Network"
+ },
+ "qrCode": {
+ "message": "Ipakita ang QR Code"
+ },
+ "readdToken": {
+ "message": "Upang muling idagdag ang token na ito, pumunta sa “Magdagdag ng Token” sa options menu ng iyong account."
+ },
+ "readMore": {
+ "message": "Alamin ang iba pang impormasyon dito."
+ },
+ "receive": {
+ "message": "Tanggapin"
+ },
+ "recipientAddress": {
+ "message": "Address ng Tatanggap"
+ },
+ "refundAddress": {
+ "message": "Ang Iyong Refund Address"
+ },
+ "rejected": {
+ "message": "Tinanggihan"
+ },
+ "required": {
+ "message": "Kailangan"
+ },
+ "retryWithMoreGas": {
+ "message": "Muling subukan ng may mas mataas na gas price dito"
+ },
+ "revert": {
+ "message": "Ibalik"
+ },
+ "rinkeby": {
+ "message": "Rinkeby Test Network"
+ },
+ "ropsten": {
+ "message": "Ropsten Test Network"
+ },
+ "sampleAccountName": {
+ "message": "Halimbawa: Ang aking bagong account",
+ "description": "Tulungan ang user na intindihin ang konsepto ng pagdagdag ng human-readable name sa kanilang account"
+ },
+ "save": {
+ "message": "I-save"
+ },
+ "saveAsFile": {
+ "message": "I-save bilang File",
+ "description": "Proseso sa pag-export ng Account"
+ },
+ "selectService": {
+ "message": "Piliin ang Service"
+ },
+ "send": {
+ "message": "Magpadala"
+ },
+ "sendTokens": {
+ "message": "Magpadala ng Tokens"
+ },
+ "sendTokensAnywhere": {
+ "message": "Magpadala ng Tokens sa sinumang may Ethereum account"
+ },
+ "settings": {
+ "message": "Mga Setting"
+ },
+ "shapeshiftBuy": {
+ "message": "Bumili gamit ang Shapeshift"
+ },
+ "showPrivateKeys": {
+ "message": "Ipakita ang Private Keys"
+ },
+ "showQRCode": {
+ "message": "Ipakita ang QR Code"
+ },
+ "sign": {
+ "message": "I-sign"
+ },
+ "signMessage": {
+ "message": "I-sign ang mensahe"
+ },
+ "signNotice": {
+ "message": "Ang pag-sign ng mensaheng ito ay maaring magdulot ng mapanganib na epekto. I-sign lamang ang mga mensahe mula sa mga site na pinagkakatiwalaan mo ng iyong account. Ang mapanganib na paraang ito ay aalisin sa isa sa mga susunod na bersyon. "
+ },
+ "sigRequest": {
+ "message": "Hiling na Signature"
+ },
+ "sigRequested": {
+ "message": "Hiniling ang Signature"
+ },
+ "status": {
+ "message": "Istado"
+ },
+ "submit": {
+ "message": "I-submit"
+ },
+ "takesTooLong": {
+ "message": "Masyadong matagal?"
+ },
+ "testFaucet": {
+ "message": "Test Faucet"
+ },
+ "to": {
+ "message": "To"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 sa ETH sa pamamagitan ng ShapeShift",
+ "description": "Pupunan ng system ang deposit type sa simula ng mensahe"
+ },
+ "tokenBalance": {
+ "message": "Ang iyong Token Balance ay:"
+ },
+ "total": {
+ "message": "Kabuuan"
+ },
+ "transactionMemo": {
+ "message": "Memo ng transaksyon (opsyonal)"
+ },
+ "transactionNumber": {
+ "message": "Numero ng Transaksyon"
+ },
+ "transfers": {
+ "message": "Mga Inilipat"
+ },
+ "troubleTokenBalances": {
+ "message": "Nagkaroon kami ng problema sa paglo-load ng iyong mga balanseng token. Tingnan ito dito ",
+ "description": "Susundan ng link (dito) para tingnan ang token balances"
+ },
+ "typePassword": {
+ "message": "I-type ang iyong Password"
+ },
+ "uiWelcome": {
+ "message": "Maligayang pagdating sa Bagong UI (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "Ginagamit mo na ngayon ang bagong MetaMask UI. I-explore at subukan ang mga bagong features tulad ng pagpapadala ng mga token, at ipaalam sa amin kung mayroon kang anumang mga isyu."
+ },
+ "unavailable": {
+ "message": "Hindi Magagamit"
+ },
+ "unknown": {
+ "message": "Hindi Alam"
+ },
+ "unknownNetwork": {
+ "message": "Hindi Alam ang Pribadong Network"
+ },
+ "unknownNetworkId": {
+ "message": "Hindi alam ang network ID"
+ },
+ "usaOnly": {
+ "message": "USA lamang",
+ "description": "Ang paggamit ng exchange na ito ay limitado sa mga tao sa loob ng Estados Unidos"
+ },
+ "usedByClients": {
+ "message": "Ginagamit ng iba't ibang mga clients"
+ },
+ "viewAccount": {
+ "message": "Tingnan ang Account"
+ },
+ "warning": {
+ "message": "Babala"
+ },
+ "whatsThis": {
+ "message": "Ano ito?"
+ },
+ "yourSigRequested": {
+ "message": "Hinihiling ang iyong signature"
+ },
+ "youSign": {
+ "message": "Ikaw ay nagsa-sign"
+ }
+}
diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json
new file mode 100644
index 000000000..c9eb178f9
--- /dev/null
+++ b/app/_locales/pt/messages.json
@@ -0,0 +1,819 @@
+{
+ "accept": {
+ "message": "Aceitar"
+ },
+ "account": {
+ "message": "Conta"
+ },
+ "accountDetails": {
+ "message": "Detalhes da Conta"
+ },
+ "accountName": {
+ "message": "Nome da Conta"
+ },
+ "address": {
+ "message": "Endereço"
+ },
+ "addCustomToken": {
+ "message": "Adicionar token customizada"
+ },
+ "addToken": {
+ "message": "Adicionar Token"
+ },
+ "addTokens": {
+ "message": "Adicionar Tokens"
+ },
+ "amount": {
+ "message": "Valor"
+ },
+ "amountPlusGas": {
+ "message": "Valor + Gas"
+ },
+ "appDescription": {
+ "message": "Extensão para o browser de Ethereum",
+ "description": "A descrição da aplicação"
+ },
+ "appName": {
+ "message": "MetaMask",
+ "description": "Nome da aplicação"
+ },
+ "attemptingConnect": {
+ "message": "A tentar ligar à blockchain."
+ },
+ "attributions": {
+ "message": "Atribuições"
+ },
+ "available": {
+ "message": "Disponível"
+ },
+ "back": {
+ "message": "Voltar"
+ },
+ "balance": {
+ "message": "Saldo:"
+ },
+ "balances": {
+ "message": "O meu saldo"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Saldo insuficiente para a quantidade de gas total"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "tem de ser maior ou igual a $1 e menor ou igual a $2.",
+ "description": "ajuda para introduzir hexadecimal como decimal"
+ },
+ "blockiesIdenticon": {
+ "message": "Usar Blockies Identicon"
+ },
+ "borrowDharma": {
+ "message": "Pedir Empréstimo Com Dharma (Beta)"
+ },
+ "builtInCalifornia": {
+ "message": "MetaMask é desenhada e construída na California."
+ },
+ "buy": {
+ "message": "Comprar"
+ },
+ "buyCoinbase": {
+ "message": "Comprar no Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase é a forma mais conhecida para comprar e vender bitcoin, ethereum, e litecoin."
+ },
+ "cancel": {
+ "message": "Cancelar"
+ },
+ "classicInterface": {
+ "message": "Utilizar interface clássico"
+ },
+ "clickCopy": {
+ "message": "Carregue para copiar"
+ },
+ "confirm": {
+ "message": "Confirmar"
+ },
+ "confirmContract": {
+ "message": "Confirmar Contrato"
+ },
+ "confirmPassword": {
+ "message": "Confirmar Palavra-passe"
+ },
+ "confirmTransaction": {
+ "message": "Confirmar Transação"
+ },
+ "continue": {
+ "message": "Continuar"
+ },
+ "continueToCoinbase": {
+ "message": "Continuar para o Coinbase"
+ },
+ "contractDeployment": {
+ "message": "Distribuição do Contrato"
+ },
+ "conversionProgress": {
+ "message": "Conversão em progresso"
+ },
+ "copiedButton": {
+ "message": "Copiado"
+ },
+ "copiedClipboard": {
+ "message": "Copiado para a Área de Transferência"
+ },
+ "copiedExclamation": {
+ "message": "Copiado!"
+ },
+ "copiedSafe": {
+ "message": "Já copiei para um lugar seguro"
+ },
+ "copy": {
+ "message": "Copiar"
+ },
+ "copyToClipboard": {
+ "message": "Copiar para o clipboard"
+ },
+ "copyButton": {
+ "message": " Copiar "
+ },
+ "copyPrivateKey": {
+ "message": "Esta é a sua chave privada (carregue para copiar)"
+ },
+ "create": {
+ "message": "Criar"
+ },
+ "createAccount": {
+ "message": "Criar Conta"
+ },
+ "createDen": {
+ "message": "Criar"
+ },
+ "crypto": {
+ "message": "Cripto",
+ "description": "Tipo de câmbio (criptomoedas)"
+ },
+ "currentConversion": {
+ "message": "Taxa de Conversão Atual"
+ },
+ "currentNetwork": {
+ "message": "Rede Atual"
+ },
+ "customGas": {
+ "message": "Customizar Gas"
+ },
+ "customize": {
+ "message": "Customizar"
+ },
+ "customRPC": {
+ "message": "Customizar RPC"
+ },
+ "decimalsMustZerotoTen": {
+ "message": "Decimais devem ser no mínimo 0 e não passar de 36."
+ },
+ "decimal": {
+ "message": "Precisão em Decimais"
+ },
+ "defaultNetwork": {
+ "message": "A rede pré definida para transações em Ether é a Main Net."
+ },
+ "denExplainer": {
+ "message": " DEN é o armazenamento encriptado da sua palavra-passe no MetaMask."
+ },
+ "deposit": {
+ "message": "Depósito"
+ },
+ "depositBTC": {
+ "message": "Deposite as suas BTC no endereço abaixo:"
+ },
+ "depositCoin": {
+ "message": "Deposite $1 no endereço abaixo",
+ "description": "Diz ao usuário que moeda selecionou para depositar com shapeshift"
+ },
+ "depositEth": {
+ "message": "Depositar Eth"
+ },
+ "depositEther": {
+ "message": "Depositar Ether"
+ },
+ "depositFiat": {
+ "message": "Depositar moeda fiduciária"
+ },
+ "depositFromAccount": {
+ "message": "Depositar de outra conta"
+ },
+ "depositShapeShift": {
+ "message": "Depositar com ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "Se tem criptomoedas, pode trocar e depositar Ether diretamente na sua carteira MetaMask. Não precisa de conta."
+ },
+ "details": {
+ "message": "Detalhes"
+ },
+ "directDeposit": {
+ "message": "Depósito Direto"
+ },
+ "directDepositEther": {
+ "message": "Depositar Diretamente Ether"
+ },
+ "directDepositEtherExplainer": {
+ "message": "Se já tem Ether, a forma mais rápida de ficar com Ether na sua carteira é através de depósito direto."
+ },
+ "done": {
+ "message": "Finalizado"
+ },
+ "downloadStatelogs": {
+ "message": "Descarregar Registos de Estado"
+ },
+ "edit": {
+ "message": "Editar"
+ },
+ "editAccountName": {
+ "message": "Editar Nome da Conta"
+ },
+ "emailUs": {
+ "message": "Fale connosco!"
+ },
+ "encryptNewDen": {
+ "message": "Encripte o seu novo DEN"
+ },
+ "enterPassword": {
+ "message": "Introduza palavra-passe"
+ },
+ "enterPasswordConfirm": {
+ "message": "Introduza a sua palavra-passe para confirmar"
+ },
+ "etherscanView": {
+ "message": "Ver conta no Etherscan"
+ },
+ "exchangeRate": {
+ "message": "Taxa de Câmbio"
+ },
+ "exportPrivateKey": {
+ "message": "Exportar Chave Privada"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "Exportar chaves privadas por sua conta e risco."
+ },
+ "failed": {
+ "message": "Falhou"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Tipo de câmbio"
+ },
+ "fileImportFail": {
+ "message": "A importação de ficheiro não está a funcionar? Carregue aqui!",
+ "description": "Ajuda usuários a importar as suas contas a partir de um ficheiro JSON"
+ },
+ "followTwitter": {
+ "message": "Siga-nos no Twitter"
+ },
+ "from": {
+ "message": "De"
+ },
+ "fromToSame": {
+ "message": "Endereços De e Para não podem ser iguais"
+ },
+ "fromShapeShift": {
+ "message": "De ShapeShift"
+ },
+ "gas": {
+ "message": "Gas",
+ "description": "Indicação breve do custo de gas"
+ },
+ "gasFee": {
+ "message": "Taxa de Gas"
+ },
+ "gasLimit": {
+ "message": "Limite de Gas"
+ },
+ "gasLimitCalculation": {
+ "message": "Calculamos o limite sugerido do gas com base nas taxas de sucesso da rede."
+ },
+ "gasLimitRequired": {
+ "message": "Limite de Gas Necessário"
+ },
+ "gasLimitTooLow": {
+ "message": "Limite de Gas deve ser no mínimo 21000"
+ },
+ "generatingSeed": {
+ "message": "A gerar Seed..."
+ },
+ "gasPrice": {
+ "message": "Preço Gas (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "Calculamos o gas sugerido com base nas taxas de sucesso da rede."
+ },
+ "gasPriceRequired": {
+ "message": "Preço Gas Necessário"
+ },
+ "getEther": {
+ "message": "Obter Ether"
+ },
+ "getEtherFromFaucet": {
+ "message": "Obter Ether de um faucet por $1",
+ "description": "Mostra nome da rede para faucet de Ether"
+ },
+ "greaterThanMin": {
+ "message": "tem de ser maior ou igual a $1.",
+ "description": "ajuda para introduzir hexadecimal como decimal"
+ },
+ "here": {
+ "message": "aqui",
+ "description": "como -clicar aqui- para mais informações (associado a troubleTokenBalances)"
+ },
+ "hereList": {
+ "message": "Aqui está uma lista!!!!"
+ },
+ "hide": {
+ "message": "Ocultar"
+ },
+ "hideToken": {
+ "message": "Ocultar Token"
+ },
+ "hideTokenPrompt": {
+ "message": "Ocultar Token?"
+ },
+ "howToDeposit": {
+ "message": "Como gostaria de depositar Ether?"
+ },
+ "holdEther": {
+ "message": "Permite ter ether & tokens, e serve como uma ponte para aplicações descentralizadas."
+ },
+ "import": {
+ "message": "Importar",
+ "description": "Botão para importar uma conta de um ficheiro selecionado"
+ },
+ "importAccount": {
+ "message": "Importar Conta"
+ },
+ "importAccountMsg": {
+ "message":"Contas importadas não irão ser associadas com a frase seed da conta criada originalmente pelo MetaMask. Saiba mais sobre contas importadas."
+ },
+ "importAnAccount": {
+ "message": "Importar uma conta"
+ },
+ "importDen": {
+ "message": "Importar DEN Existente"
+ },
+ "imported": {
+ "message": "Importado",
+ "description": "estado para mostrar que uma conta foi totalmente carregada para o keyring"
+ },
+ "infoHelp": {
+ "message": "Informação & Ajuda"
+ },
+ "insufficientFunds": {
+ "message": "Fundos insuficientes."
+ },
+ "insufficientTokens": {
+ "message": "Tokens insuficientes."
+ },
+ "invalidAddress": {
+ "message": "Endereço inválido"
+ },
+ "invalidAddressRecipient": {
+ "message": "O endereço do destinatário é inválido "
+ },
+ "invalidGasParams": {
+ "message": "Parâmetros para o Gas Inválidos"
+ },
+ "invalidInput": {
+ "message": "Campo inválido."
+ },
+ "invalidRequest": {
+ "message": "Pedido Inválido"
+ },
+ "invalidRPC": {
+ "message": "RPC URI Inválido"
+ },
+ "jsonFail": {
+ "message": "Ocorreu um erro. Por favor confirme que o seu ficheiro JSON está devidamente formatado."
+ },
+ "jsonFile": {
+ "message": "Ficheiro JSON",
+ "description": "Formatar para importar uma conta"
+ },
+ "kovan": {
+ "message": "Rede de Teste Kovan"
+ },
+ "knowledgeDataBase": {
+ "message": "Visite o nosso Centro de Conhecimento"
+ },
+ "lessThanMax": {
+ "message": "tem de ser menor ou igual a $1.",
+ "description": "ajuda para introduzir hexadecimal como decimal"
+ },
+ "likeToAddTokens": {
+ "message": "Gostaria de adicionar estes tokens?"
+ },
+ "limit": {
+ "message": "Limite"
+ },
+ "loading": {
+ "message": "A carregar..."
+ },
+ "loadingTokens": {
+ "message": "A carregar Tokens..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "login": {
+ "message": "Entrar"
+ },
+ "logout": {
+ "message": "Sair"
+ },
+ "loose": {
+ "message": "Vago"
+ },
+ "loweCaseWords": {
+ "message": "palavras da seed apenas têm caracteres minúsculos"
+ },
+ "mainnet": {
+ "message": "Rede Principal de Ethereum"
+ },
+ "message": {
+ "message": "Mensagem"
+ },
+ "metamaskDescription": {
+ "message": "O MetaMask é um lugar seguro para guardar a sua identidade em em Ethereum."
+ },
+ "min": {
+ "message": "Mínimo"
+ },
+ "myAccounts": {
+ "message": "As minhas contas"
+ },
+ "mustSelectOne": {
+ "message": "Deve escolher no mínimo 1 token."
+ },
+ "needEtherInWallet": {
+ "message": "Para interagir com applicações descentralizadas usando MetaMask tem de ter Ether na sua carteira."
+ },
+ "needImportFile": {
+ "message": "Deve selecionar um ficheiro para importar.",
+ "description": "O utilizador deve adicionar um ficheiro para continuar"
+ },
+ "needImportPassword": {
+ "message": "Deve introduzir uma palavra-passe para o ficheiro selecionado.",
+ "description": "Palavra-passe e ficheiro necessários para importar uma conta"
+ },
+ "negativeETH": {
+ "message": "Não é possível enviar valores negativos de ETH."
+ },
+ "networks": {
+ "message": "Redes"
+ },
+ "newAccount": {
+ "message": "Conta Nova"
+ },
+ "newAccountNumberName": {
+ "message": "Conta $1",
+ "description": "Nome padrão da próxima conta a ser criado em Criar Conta"
+ },
+ "newContract": {
+ "message": "Contrato Novo"
+ },
+ "newPassword": {
+ "message": "Nova Palavra-passe (min 8 caracteres)"
+ },
+ "newRecipient": {
+ "message": "Recipiente Novo"
+ },
+ "newRPC": {
+ "message": "Novo RPC URL"
+ },
+ "next": {
+ "message": "Próximo"
+ },
+ "noAddressForName": {
+ "message": "Nenhum endereço foi estabelecido para este nome."
+ },
+ "noDeposits": {
+ "message": "Sem depósitos recebidos"
+ },
+ "noTransactionHistory": {
+ "message": "Sem histórico de transações."
+ },
+ "noTransactions": {
+ "message": "Sem Transações"
+ },
+ "notStarted": {
+ "message": "Não Iniciado"
+ },
+ "oldUI": {
+ "message": "UI Antigo"
+ },
+ "oldUIMessage": {
+ "message": "Voltou para o UI antigo. Pode reverter para o Novo UI através da opção no menu do topo direito."
+ },
+ "or": {
+ "message": "ou",
+ "description": "opção entre criar ou importar uma nova conta"
+ },
+ "passwordCorrect": {
+ "message": "Por favor confirme que a sua palavra-passe esteja correta."
+ },
+ "passwordMismatch": {
+ "message": "as palavras-passe não coincidem",
+ "description": "no processo de criação da palavra-passe, as duas palavras-passe não coincidiram"
+ },
+ "passwordShort": {
+ "message": "palavra-passe deve ser mais comprida",
+ "description": "no processo de criação da palavra-passe, a palavra-apasse não é longa o suficiente para ser segura"
+ },
+ "pastePrivateKey": {
+ "message": "Cole aqui a sua chave privada:",
+ "description": "Para importar uma conta através da chave privada"
+ },
+ "pasteSeed": {
+ "message": "Cole aqui a sua frase seed!"
+ },
+ "personalAddressDetected": {
+ "message": "Endereço pessoal detectado. Introduza o endereço do contrato do token."
+ },
+ "pleaseReviewTransaction": {
+ "message": "Por favor reveja a sua transação."
+ },
+ "privacyMsg": {
+ "message": "Política de Privacidade"
+ },
+ "privateKey": {
+ "message": "Chave Privada",
+ "description": "Selecione este tipo de ficheiro para importar uma conta"
+ },
+ "privateKeyWarning": {
+ "message": "Atenção: Nunca revele esta chave. Qualquer pessoa com acesso à sua chave privada pode roubar os bens que esta contém."
+ },
+ "privateNetwork": {
+ "message": "Rede Privada"
+ },
+ "qrCode": {
+ "message": "Mostrar Código QR"
+ },
+ "readdToken": {
+ "message": "Pode adicionar este token de novo clicando na opção “Adicionar token” no menu de opções da sua conta."
+ },
+ "readMore": {
+ "message": "Ler mais aqui."
+ },
+ "readMore2": {
+ "message": "Ler mais."
+ },
+ "receive": {
+ "message": "Receber"
+ },
+ "recipientAddress": {
+ "message": "Endereço do Destinatário"
+ },
+ "refundAddress": {
+ "message": "O seu endereço de reembolso"
+ },
+ "rejected": {
+ "message": "Rejeitado"
+ },
+ "resetAccount": {
+ "message": "Reinicializar Conta"
+ },
+ "restoreFromSeed": {
+ "message": "Restaurar a partir da frase seed"
+ },
+ "required": {
+ "message": "Necessário"
+ },
+ "retryWithMoreGas": {
+ "message": "Tentar novamente com um preço mais elevado aqui"
+ },
+ "revealSeedWords": {
+ "message": "Revelar Palavras Seed"
+ },
+ "revealSeedWordsWarning": {
+ "message": "Não revele as palavras seed num espaço público! Estas palavras podem ser usadas para roubar todas as suas contas."
+ },
+ "revert": {
+ "message": "Reverter"
+ },
+ "rinkeby": {
+ "message": "Rede de Teste Rinkeby"
+ },
+ "ropsten": {
+ "message": "Rede de Teste Ropsten"
+ },
+ "sampleAccountName": {
+ "message": "Ex. A minha conta nova",
+ "description": "Ajuda o utilizador a perceber o conceito de adicionar um nome legível à sua conta"
+ },
+ "save": {
+ "message": "Guardar"
+ },
+ "saveAsFile": {
+ "message": "Guardar como Ficheiro",
+ "description": "Processo de exportação de conta"
+ },
+ "saveSeedAsFile": {
+ "message": "Guardar Palavras Seed como um Ficheiro"
+ },
+ "search": {
+ "message": "Procurar"
+ },
+ "secretPhrase": {
+ "message": "Introduza a sua frase secreta de 12 palavras para recuperar o seu ."
+ },
+ "seedPhraseReq": {
+ "message": "seed phrases are 12 words long"
+ },
+ "select": {
+ "message": "Selecionar"
+ },
+ "selectCurrency": {
+ "message": "Selecionar Moeda"
+ },
+ "selectService": {
+ "message": "Selecionar Serviço"
+ },
+ "selectType": {
+ "message": "Selecionar Tipo"
+ },
+ "send": {
+ "message": "Enviar"
+ },
+ "sendETH": {
+ "message": "Enviar ETH"
+ },
+ "sendTokens": {
+ "message": "Enviar Tokens"
+ },
+ "sendTokensAnywhere": {
+ "message": "Enviar Tokens para qualquer pessoa com uma conta Ethereum"
+ },
+ "settings": {
+ "message": "Definições"
+ },
+ "shapeshiftBuy": {
+ "message": "Comprar com Shapeshift"
+ },
+ "showPrivateKeys": {
+ "message": "Mostrar Chaves Privadas"
+ },
+ "showQRCode": {
+ "message": "Mostrar Código QR"
+ },
+ "sign": {
+ "message": "Assinar"
+ },
+ "signMessage": {
+ "message": "Assinar Mensagem"
+ },
+ "signNotice": {
+ "message": "Assinar esta mensagem pode ter \nefeitos laterais perigosos. Apenas assine mensagens de sites que \ntotalmente confia com a sua conta total.\n Este método perigoso será removido numa versão posterior."
+ },
+ "sigRequest": {
+ "message": "Pedido de Assinatura"
+ },
+ "sigRequested": {
+ "message": "Assinatura Pedida"
+ },
+ "spaceBetween": {
+ "message": "só pode haver um espaço entre palavras"
+ },
+ "status": {
+ "message": "Estado"
+ },
+ "stateLogs": {
+ "message": "Registos de Estado"
+ },
+ "stateLogsDescription": {
+ "message": "Registo de estado podem conter o seu endereço e transações enviadas da sua conta pública."
+ },
+ "submit": {
+ "message": "Submeter"
+ },
+ "supportCenter": {
+ "message": "Visitar o nosso Centro de Suporte"
+ },
+ "symbolBetweenZeroTen": {
+ "message": "Símbolo deve conter entre 0 e 10 characters."
+ },
+ "takesTooLong": {
+ "message": "A demorar muito?"
+ },
+ "terms": {
+ "message": "Termos de Uso"
+ },
+ "testFaucet": {
+ "message": "Faucet de Teste"
+ },
+ "to": {
+ "message": "Para"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 para ETH via ShapeShift",
+ "description": "o sistema irá preencher o tipo de depósito no início da mensagem"
+ },
+ "tokenAddress": {
+ "message": "Endereço do Token"
+ },
+ "tokenAlreadyAdded": {
+ "message": "Token já foi adicionado."
+ },
+ "tokenBalance": {
+ "message": "O seu balanço é:"
+ },
+ "tokenSelection": {
+ "message": "Procure por tokens ou seleccione da nossa lista de tokens populares."
+ },
+ "tokenSymbol": {
+ "message": "Símbolo do Token"
+ },
+ "tokenWarning1": {
+ "message": "Registe os tokens que comprou com a sua conta MetaMask. Se comprou tokens utilizando uma conta diferente, esses tokens não irão aparecer aqui."
+ },
+ "total": {
+ "message": "Total"
+ },
+ "transactions": {
+ "message": "transações"
+ },
+ "transactionMemo": {
+ "message": "Notas da transação (opcional)"
+ },
+ "transactionNumber": {
+ "message": "Número da Transação"
+ },
+ "transfers": {
+ "message": "Transferências"
+ },
+ "troubleTokenBalances": {
+ "message": "Tivemos um problema a carregar o balanço dos seus tokens. Pode vê-los em ",
+ "description": "Seguido de um link (aqui) para ver o balanço dos seus tokens"
+ },
+ "twelveWords": {
+ "message": "Estas 12 palavras são a única forma de recuperar as suas contas na MetaMask.\nGuarde-as num local seguro e secreto."
+ },
+ "typePassword": {
+ "message": "Digite a sua Palavra-passe"
+ },
+ "uiWelcome": {
+ "message": "Bem-vindo ao seu Novo UI (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "Está agora a usar o novo UI da MetaMask. Dê uma vista de olhos, experimenta as novas funcionalidades como enviar tokens e diga-nos se tiver algum problema."
+ },
+ "unavailable": {
+ "message": "Indisponível"
+ },
+ "unknown": {
+ "message": "Desconhecido"
+ },
+ "unknownNetwork": {
+ "message": "Rede Privada Desconhecida"
+ },
+ "unknownNetworkId": {
+ "message": "Identificador da rede desconhecido"
+ },
+ "uriErrorMsg": {
+ "message": "Links requerem o prefixo HTTP/HTTPS apropriado."
+ },
+ "usaOnly": {
+ "message": "Só nos EUA",
+ "description": "Usar esta taxa de câmbio está limitado a pessoas residentes nos EUA"
+ },
+ "usedByClients": {
+ "message": "Utilizado por vários tipos de clientes"
+ },
+ "useOldUI": {
+ "message": "Utilizar UI antigo"
+ },
+ "validFileImport": {
+ "message": "Deve selecionar um ficheiro válido para importar."
+ },
+ "vaultCreated": {
+ "message": "Cofre Criado"
+ },
+ "viewAccount": {
+ "message": "Ver Conta"
+ },
+ "visitWebSite": {
+ "message": "Visite o nosso site"
+ },
+ "warning": {
+ "message": "Aviso"
+ },
+ "welcomeBeta": {
+ "message": "Bem-vindo ao MetaMask Beta"
+ },
+ "whatsThis": {
+ "message": "O que é isto?"
+ },
+ "yourSigRequested": {
+ "message": "A sua assinatura está a ser pedida"
+ },
+ "youSign": {
+ "message": "Está a assinar"
+ }
+}
diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json
new file mode 100644
index 000000000..cd30de4de
--- /dev/null
+++ b/app/_locales/vi/messages.json
@@ -0,0 +1,609 @@
+{
+ "accept": {
+ "message": "Chấp nhận"
+ },
+ "account": {
+ "message": "Tài khoản"
+ },
+ "accountDetails": {
+ "message": "Chi tiết tài khoản"
+ },
+ "accountName": {
+ "message": "Tên tài khoản"
+ },
+ "addToken": {
+ "message": "Thêm mã Token"
+ },
+ "address": {
+ "message": "Địa chỉ"
+ },
+ "amount": {
+ "message": "Số lượng"
+ },
+ "amountPlusGas": {
+ "message": "Số lượng + Gas"
+ },
+ "appDescription": {
+ "description": "Mô tả phần mềm",
+ "message": "Tính năng Ethereum cho trình duyệt"
+ },
+ "appName": {
+ "description": "Tên phần mềm",
+ "message": "MetaMask"
+ },
+ "attemptingConnect": {
+ "message": "Đang kết nối đến blockchain."
+ },
+ "available": {
+ "message": "Có sẵn"
+ },
+ "back": {
+ "message": "Quay lại"
+ },
+ "balance": {
+ "message": "Số dư:"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Số dư không đủ để thanh toán tổng tiền gas hiện tại"
+ },
+ "beta": {
+ "message": "Bản thử nghiệm Beta"
+ },
+ "betweenMinAndMax": {
+ "description": "trợ giúp để nhập hex dưới dạng số thập phân",
+ "message": "phải nhiều hơn hoặc bằng $1 và ít hơn hoặc bằng $2."
+ },
+ "borrowDharma": {
+ "message": "Vay Dharma (thử nghiệm)"
+ },
+ "buy": {
+ "message": "Mua"
+ },
+ "buyCoinbase": {
+ "message": "Mua trên Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase là dịch vụ ví điện tử thông dụng nhất thế giới để mua bán Bitcoin, Ethereum, và Litecoin"
+ },
+ "cancel": {
+ "message": "Hủy"
+ },
+ "clickCopy": {
+ "message": "Nhấp vào để sao chép"
+ },
+ "confirm": {
+ "message": "Xác nhận"
+ },
+ "confirmContract": {
+ "message": "Xác nhận hợp đồng"
+ },
+ "confirmPassword": {
+ "message": "Xác nhận mật khẩu"
+ },
+ "confirmTransaction": {
+ "message": "Xác nhận giao dịch"
+ },
+ "continueToCoinbase": {
+ "message": "Tiếp tục đến Coinbase"
+ },
+ "contractDeployment": {
+ "message": "Triển khai hợp đồng"
+ },
+ "conversionProgress": {
+ "message": "Đang chuyển đổi"
+ },
+ "copiedButton": {
+ "message": "Đã sao chép"
+ },
+ "copiedClipboard": {
+ "message": "Đã sao chép vào bộ nhớ Clipboard"
+ },
+ "copiedExclamation": {
+ "message": "Đã sao chép!"
+ },
+ "copy": {
+ "message": "Sao chép"
+ },
+ "copyButton": {
+ "message": "Sao chép"
+ },
+ "copyPrivateKey": {
+ "message": "Đây là Khoá Bí Mật của bạn (nhấp vào để sao chép)"
+ },
+ "copyToClipboard": {
+ "message": "Đã sao chép vào clipboard"
+ },
+ "create": {
+ "message": "Tạo"
+ },
+ "createAccount": {
+ "message": "Tạo tài khoản"
+ },
+ "createDen": {
+ "message": "Tạo"
+ },
+ "crypto": {
+ "description": "Hình thức giao dịch (tiền điện tử)",
+ "message": "Tiền điện tử"
+ },
+ "customGas": {
+ "message": "Tùy chỉnh gas"
+ },
+ "customRPC": {
+ "message": "Tùy chỉnh RPC"
+ },
+ "customize": {
+ "message": "Tùy chỉnh"
+ },
+ "defaultNetwork": {
+ "message": "Mạng lưới mặc định dùng cho các giao dịch Ether là Main Net (tiền ETH thật)."
+ },
+ "denExplainer": {
+ "message": "Số DEN của bạn là mật khẩu đã được mã hóa và lưu trữ trong MetaMask"
+ },
+ "deposit": {
+ "message": "Ký gửi/nạp tiền"
+ },
+ "depositBTC": {
+ "message": "Ký gửi BTC đến địa chỉ sau:"
+ },
+ "depositCoin": {
+ "description": "Báo cho người dùng biết đồng tiền nào họ đã chọn để ký gửi với ShapeShift",
+ "message": "Ký gửi $1 đến địa chỉ sau:"
+ },
+ "depositEth": {
+ "message": "Ký gửi Eth"
+ },
+ "depositEther": {
+ "message": "Ký gửi Ether"
+ },
+ "depositFiat": {
+ "message": "Ký gửi bằng tiền Fiat (USD, GBP, ...)"
+ },
+ "depositFromAccount": {
+ "message": "Ký gửi từ một tài khoản khác"
+ },
+ "depositShapeShift": {
+ "message": "Ký gửi với ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "Nếu bạn sở hữu các đồng tiền điện tử khác, bạn có thể giao dịch và ký gửi Ether trực tiếp vào ví MetaMask của bạn. Không cần tạo thêm tài khoản."
+ },
+ "details": {
+ "message": "Chi tiết"
+ },
+ "directDeposit": {
+ "message": "Ký gửi trực tiếp"
+ },
+ "directDepositEther": {
+ "message": "Ký gửi Ether trực tiếp"
+ },
+ "directDepositEtherExplainer": {
+ "message": "Nếu bạn đã có sẵn vài Ether, cách nhanh nhất để thêm Ether vào ví tiền mới của bạn là bằng ký gửi trực tiếp."
+ },
+ "done": {
+ "message": "Hoàn tất"
+ },
+ "edit": {
+ "message": "Chỉnh sửa"
+ },
+ "editAccountName": {
+ "message": "Chỉnh sửa tên tài khoản"
+ },
+ "encryptNewDen": {
+ "message": "Mã hóa số DEN mới của bạn"
+ },
+ "enterPassword": {
+ "message": "Nhập mật khẩu"
+ },
+ "etherscanView": {
+ "message": "Xem tài khoản trên Etherscan"
+ },
+ "exchangeRate": {
+ "message": "Tỷ giá"
+ },
+ "exportPrivateKey": {
+ "message": "Xuất mã khóa cá nhân"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "Xuất mã khóa cá nhân theo rủi ro tự chịu"
+ },
+ "failed": {
+ "message": "Không thành công"
+ },
+ "fiat": {
+ "description": "Hình thức giao dịch",
+ "message": "FIAT"
+ },
+ "fileImportFail": {
+ "description": "Giúp người dùng nhập tài khoản từ tập tin JSON",
+ "message": "Tập tin đã nhập không hoạt động? Nhấp vào đây!"
+ },
+ "from": {
+ "message": "Từ"
+ },
+ "fromShapeShift": {
+ "message": "Từ ShapeShift"
+ },
+ "gas": {
+ "description": "Viết tắt của giá ga",
+ "message": "Ga"
+ },
+ "gasFee": {
+ "message": "Tiền ga"
+ },
+ "gasLimit": {
+ "message": "Hạn mức ga"
+ },
+ "gasLimitCalculation": {
+ "message": "Chúng tôi tính toán và gợi ý một hạn mức ga cụ thể dựa trên tỷ lệ thành công của hệ thống."
+ },
+ "gasLimitRequired": {
+ "message": "Hạn mức ga được yêu cầu"
+ },
+ "gasLimitTooLow": {
+ "message": "Hạn mức ga phải đạt tối thiểu 21000"
+ },
+ "gasPrice": {
+ "message": "Giá ga (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "Chúng tôi tính toán và gợi ý một giá ga cụ thể dựa trên tỷ lệ thành công của hệ thống."
+ },
+ "gasPriceRequired": {
+ "message": "Giá ga được yêu cầu"
+ },
+ "getEther": {
+ "message": "Lấy Ether"
+ },
+ "getEtherFromFaucet": {
+ "description": "Hiển thị tên mạng cho vòi Ether",
+ "message": "Lấy Ether từ vòi với giá $1"
+ },
+ "greaterThanMin": {
+ "description": "trợ giúp để nhập hex dưới dạng số thập phân",
+ "message": "phải nhiều hơn hoặc bằng $1"
+ },
+ "here": {
+ "description": "như trong -Nhấp vào đây để xem thêm thông tin- (dùng với các số dư token có vấn đề)",
+ "message": "tại đây"
+ },
+ "hide": {
+ "message": "Ẩn"
+ },
+ "hideToken": {
+ "message": "Ẩn mã token"
+ },
+ "hideTokenPrompt": {
+ "message": "Ẩn mã token?"
+ },
+ "howToDeposit": {
+ "message": "Bạn muốn ký gửi Ether bằng cách nào?"
+ },
+ "import": {
+ "description": "Nút để nhập tài khoản từ một tập tin đã chọn",
+ "message": "Nhập"
+ },
+ "importAccount": {
+ "message": "Nhập tài khoản"
+ },
+ "importAnAccount": {
+ "message": "Nhập một tài khoản"
+ },
+ "importDen": {
+ "message": "Nhập mã DEN hiện hành"
+ },
+ "imported": {
+ "description": "Trạng thái hiển thị cho một tài khoản đã được tải toàn bộ vào chùm chìa khóa",
+ "message": "Đã nhập"
+ },
+ "infoHelp": {
+ "message": "Thông tin & Trợ giúp"
+ },
+ "invalidAddress": {
+ "message": "Địa chỉ không hợp lệ"
+ },
+ "invalidGasParams": {
+ "message": "Thông số ga không hợp lệ"
+ },
+ "invalidInput": {
+ "message": "Thông tin nhập vào không hợp lệ"
+ },
+ "invalidRequest": {
+ "message": "Yêu cầu không hợp lệ"
+ },
+ "jsonFile": {
+ "description": "định dạng để nhập tài khoản",
+ "message": "Tập tin JSON"
+ },
+ "kovan": {
+ "message": "Mạng thử nghiệm Kovan"
+ },
+ "lessThanMax": {
+ "description": "trợ giúp để nhập hex dưới dạng số thập phân",
+ "message": "phải ít hơn hoặc bằng $1."
+ },
+ "limit": {
+ "message": "Giới hạn"
+ },
+ "loading": {
+ "message": "Đang tải..."
+ },
+ "loadingTokens": {
+ "message": "Đang tải mã token..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "logout": {
+ "message": "Thoát"
+ },
+ "loose": {
+ "message": "Nới lỏng"
+ },
+ "mainnet": {
+ "message": "Mạng Ethereum MainNet (tiền ETH thật)"
+ },
+ "message": {
+ "message": "Tin nhắn"
+ },
+ "min": {
+ "message": "Tối thiểu"
+ },
+ "myAccounts": {
+ "message": "Tài khoản của tôi"
+ },
+ "needEtherInWallet": {
+ "message": "Để tương tác với các ứng dụng phân tán bằng MetaMask, bạn sẽ phải cần có Ether trong ví của bạn."
+ },
+ "needImportFile": {
+ "description": "Người dùng đang nhập một tài khoản và cần thêm tập tin để tiếp tục",
+ "message": "Bạn phải chọn một tập tin để nhập."
+ },
+ "needImportPassword": {
+ "description": "Mật khẩu và tập tin cần thiết để nhập một tài khoản",
+ "message": "Bạn phải điền mật khẩu cho tập tin đã chọn."
+ },
+ "networks": {
+ "message": "Các mạng lưới"
+ },
+ "newAccount": {
+ "message": "Tài khoản mới"
+ },
+ "newAccountNumberName": {
+ "description": "Tên mặc định của tài khoản tiếp theo để tạo trên màn hình tài khoản",
+ "message": "Tài khoản $1"
+ },
+ "newContract": {
+ "message": "Hợp đồng mới"
+ },
+ "newPassword": {
+ "message": "Mật khẩu mới (tối thiểu 8 ký tự)"
+ },
+ "newRecipient": {
+ "message": "Người nhận mới"
+ },
+ "next": {
+ "message": "Kế tiếp"
+ },
+ "noAddressForName": {
+ "message": "Không có địa chỉ nào được ấn định cho tên này."
+ },
+ "noDeposits": {
+ "message": "Không có tiền ký gửi nào được nhận"
+ },
+ "noTransactionHistory": {
+ "message": "Không có lịch sử giao dịch"
+ },
+ "noTransactions": {
+ "message": "Không có giao dịch"
+ },
+ "notStarted": {
+ "message": "Chưa bắt đầu"
+ },
+ "oldUI": {
+ "message": "Giao diện cũ"
+ },
+ "oldUIMessage": {
+ "message": "Bạn vừa quay về giao diện cũ. Bạn có thể chuyển đổi sang giao diện mới bằng tùy chọn trong menu ở góc phải trên cùng."
+ },
+ "or": {
+ "description": "lựa chọn giữa tạo hay nhập một tài khoản mới",
+ "message": "hoặc"
+ },
+ "passwordMismatch": {
+ "description": "trong quá trình tạo mật khẩu, hai trường mật khẩu mới không khớp",
+ "message": "mật khẩu không khớp"
+ },
+ "passwordShort": {
+ "description": "trong quá trình tạo mật khẩu, mật khẩu không đủ dài để đảm bảo an toàn",
+ "message": "mật khẩu không đủ dài"
+ },
+ "pastePrivateKey": {
+ "description": "Dùng cho việc nhập tài khoản từ một khóa cá nhân",
+ "message": "Dán dãy khóa cá nhân của bạn tại đây:"
+ },
+ "pasteSeed": {
+ "message": "Dán Mật Khẩu Sinh Khoá (seed phrase) của bạn tại đây!"
+ },
+ "pleaseReviewTransaction": {
+ "message": "Vui lòng xem lại giao dịch của bạn."
+ },
+ "privateKey": {
+ "description": "chọn định dạng tập tin này để nhập một tài khoản",
+ "message": "Khóa Bí Mật"
+ },
+ "privateKeyWarning": {
+ "message": "Cảnh báo: Không bao giờ được tiết lộ khóa này. Bất kỳ ai có Khóa Bí Mật của bạn đều có thể đánh cắp tài sản được giữ trong tài khoản của bạn."
+ },
+ "privateNetwork": {
+ "message": "Mạng lưới riêng"
+ },
+ "qrCode": {
+ "message": "Hiển thị mã QR"
+ },
+ "readMore": {
+ "message": "Đọc thêm tại đây."
+ },
+ "readdToken": {
+ "message": "Bạn có thể thêm trở lại mã token này bằng cách nhấn \"Thêm mã token\" trong menu tùy chọn trong tài khoản của bạn."
+ },
+ "receive": {
+ "message": "Nhận"
+ },
+ "recipientAddress": {
+ "message": "Địa chỉ người nhận"
+ },
+ "refundAddress": {
+ "message": "Địa chỉ hoàn trả lại của bạn"
+ },
+ "rejected": {
+ "message": "Không chấp thuận/Bị từ chối"
+ },
+ "required": {
+ "message": "Yêu cầu"
+ },
+ "retryWithMoreGas": {
+ "message": "Thử lại với một giá ga cao hơn tại đây"
+ },
+ "revert": {
+ "message": "Chuyển lại"
+ },
+ "rinkeby": {
+ "message": "Mạng thử nghiệm Rinkeby"
+ },
+ "ropsten": {
+ "message": "Mạng thử nghiệm Ropsten"
+ },
+ "sampleAccountName": {
+ "description": "Giúp người dùng hiểu khái niệm thêm một cái tên có thể đọc được vào tài khoản của họ",
+ "message": "Ví dụ, Tài khoản mới của tôi"
+ },
+ "save": {
+ "message": "Lưu"
+ },
+ "saveAsFile": {
+ "description": "Quá trình xuất tài khoản",
+ "message": "Lưu lại dưới dạng tập tin"
+ },
+ "selectService": {
+ "message": "Chọn dịch vụ"
+ },
+ "send": {
+ "message": "Gửi"
+ },
+ "sendTokens": {
+ "message": "Gửi mã token"
+ },
+ "sendTokensAnywhere": {
+ "message": "Gửi mã token đến bất kỳ ai có tài khoản Ethereum"
+ },
+ "settings": {
+ "message": "Cài đặt"
+ },
+ "shapeshiftBuy": {
+ "message": "Mua với ShapeShift"
+ },
+ "showPrivateKeys": {
+ "message": "Hiển thị khóa cá nhân"
+ },
+ "showQRCode": {
+ "message": "Hiển thị mã QR"
+ },
+ "sigRequest": {
+ "message": "Yêu cầu chữ ký"
+ },
+ "sigRequested": {
+ "message": "Đã yêu cầu chữ ký"
+ },
+ "sign": {
+ "message": "Ký nhận"
+ },
+ "signMessage": {
+ "message": "Ký nhận tin nhắn"
+ },
+ "signNotice": {
+ "message": "Ký nhận vào tin nhắn này có thể gây nguy hiểm. Chỉ nên ký nhận tin nhắn từ những nguồn bạn hoàn toàn tin tưởng có thể dùng với tài khoản của bạn. Cách thức nguy hiểm này sẽ bị xóa trong phiên bản sắp tới."
+ },
+ "status": {
+ "message": "Trạng thái"
+ },
+ "submit": {
+ "message": "Gửi đi"
+ },
+ "takesTooLong": {
+ "message": "Chờ quá lâu?"
+ },
+ "testFaucet": {
+ "message": "Vòi nhận tiền ETH ảo để thử nghiệm"
+ },
+ "to": {
+ "message": "Đến"
+ },
+ "toETHviaShapeShift": {
+ "description": "hệ thống sẽ điền vào loại tiền gửi khi bắt đầu tin nhắn",
+ "message": "$1 thành ETH qua ShapeShift"
+ },
+ "tokenBalance": {
+ "message": "Số dư token của bạn là:"
+ },
+ "total": {
+ "message": "Tổng cộng"
+ },
+ "transactionMemo": {
+ "message": "Ghi nhớ giao dịch (tùy chọn)"
+ },
+ "transactionNumber": {
+ "message": "Số thứ tự giao dịch"
+ },
+ "transfers": {
+ "message": "Các giao dịch"
+ },
+ "troubleTokenBalances": {
+ "description": "Kèm theo một đường link (tại đây) để xem số dư token.",
+ "message": "Chúng tôi gặp sự cố khi tải số dư token của bạn. Xin vui lòng xem lại"
+ },
+ "typePassword": {
+ "message": "Điền mật khẩu của bạn"
+ },
+ "uiWelcome": {
+ "message": "Chào mừng bạn đến với giao diện mới (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "Bạn đang sử dụng giao diện mới của Metamask. Chúng tôi khuyến khích bạn thử nghiệm và khám phá các tính năng mới như gửi token, và nếu bạn có gặp phải vấn đề gì khó khăn, xin hãy liên hệ ngay để chúng tôi có thể giúp đỡ bạn."
+ },
+ "unavailable": {
+ "message": "Không có sẵn"
+ },
+ "unknown": {
+ "message": "Không xác định"
+ },
+ "unknownNetwork": {
+ "message": "Mạng lưới riêng không xác định"
+ },
+ "unknownNetworkId": {
+ "message": "ID không xác định"
+ },
+ "usaOnly": {
+ "description": "Việc sử dụng hệ thống giao dịch này chỉ giới hạn cho người dùng ở Mỹ",
+ "message": "Chỉ áp dụng cho người dùng ở Mỹ"
+ },
+ "usedByClients": {
+ "message": "Được sử dụng bởi nhiều khách hàng khác nhau"
+ },
+ "viewAccount": {
+ "message": "Xem tài khoản"
+ },
+ "warning": {
+ "message": "Cảnh báo"
+ },
+ "whatsThis": {
+ "message": "Đây là gì?"
+ },
+ "youSign": {
+ "message": "Bạn đang ký nhận"
+ },
+ "yourSigRequested": {
+ "message": "Chữ ký của bạn đang được yêu cầu"
+ }
+}
diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json
index fc87384e5..203ab1923 100644
--- a/app/_locales/zh_CN/messages.json
+++ b/app/_locales/zh_CN/messages.json
@@ -1,10 +1,609 @@
{
+ "accept": {
+ "message": "接受"
+ },
+ "account": {
+ "message": "账户"
+ },
+ "accountDetails": {
+ "message": "账户详情"
+ },
+ "accountName": {
+ "message": "账户名称"
+ },
+ "address": {
+ "message": "地址"
+ },
+ "addToken": {
+ "message": "添加代币"
+ },
+ "amount": {
+ "message": "数量"
+ },
+ "amountPlusGas": {
+ "message": "数量 + Gas"
+ },
+ "appDescription": {
+ "message": "以太坊浏览器插件",
+ "description": "The description of the application"
+ },
"appName": {
"message": "MetaMask",
"description": "The name of the application"
},
- "appDescription": {
- "message": "以太坊身份管理",
- "description": "The description of the application"
+ "attemptingConnect": {
+ "message": "正在尝试连接区块链。"
+ },
+ "available": {
+ "message": "可用"
+ },
+ "back": {
+ "message": "返回"
+ },
+ "balance": {
+ "message": "余额:"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "当前余额不足以支付 Gas"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "必须大于等于 $1 并且小于等于 $2 。",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "borrowDharma": {
+ "message": "Borrow With Dharma (Beta)"
+ },
+ "buy": {
+ "message": "购买"
+ },
+ "buyCoinbase": {
+ "message": "在 Coinbase 上购买"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase 是世界上最流行的买卖比特币,以太币和莱特币的交易所。"
+ },
+ "cancel": {
+ "message": "取消"
+ },
+ "clickCopy": {
+ "message": "点击复制"
+ },
+ "confirm": {
+ "message": "确认"
+ },
+ "confirmContract": {
+ "message": "确认合约"
+ },
+ "confirmPassword": {
+ "message": "确认密码"
+ },
+ "confirmTransaction": {
+ "message": "确认交易"
+ },
+ "continueToCoinbase": {
+ "message": "继续访问 Coinbase"
+ },
+ "contractDeployment": {
+ "message": "合约部署"
+ },
+ "conversionProgress": {
+ "message": "正在进行转换"
+ },
+ "copiedButton": {
+ "message": "已复制"
+ },
+ "copiedClipboard": {
+ "message": "已复制到剪贴板"
+ },
+ "copiedExclamation": {
+ "message": "已复制!"
+ },
+ "copy": {
+ "message": "复制"
+ },
+ "copyToClipboard": {
+ "message": "复制到剪贴板"
+ },
+ "copyButton": {
+ "message": " 复制 "
+ },
+ "copyPrivateKey": {
+ "message": "这是你的私钥(点击复制)"
+ },
+ "create": {
+ "message": "创建"
+ },
+ "createAccount": {
+ "message": "创建账户"
+ },
+ "createDen": {
+ "message": "创建"
+ },
+ "crypto": {
+ "message": "加密",
+ "description": "Exchange type (cryptocurrencies)"
+ },
+ "customGas": {
+ "message": "自定义 Gas"
+ },
+ "customize": {
+ "message": "自定义"
+ },
+ "customRPC": {
+ "message": "自定义 RPC"
+ },
+ "defaultNetwork": {
+ "message": "默认以太坊交易网络为主网。"
+ },
+ "denExplainer": {
+ "message": "你的 DEN 是存储在 MetaMask 中的已加密密码。"
+ },
+ "deposit": {
+ "message": "存入"
+ },
+ "depositBTC": {
+ "message": "将你的 BTC 存入到下面的地址:"
+ },
+ "depositCoin": {
+ "message": "将你的 $1 存入到下面的地址",
+ "description": "Tells the user what coin they have selected to deposit with shapeshift"
+ },
+ "depositEth": {
+ "message": "存入 Eth"
+ },
+ "depositEther": {
+ "message": "存入 Ether"
+ },
+ "depositFiat": {
+ "message": "从 Fiat 存入"
+ },
+ "depositFromAccount": {
+ "message": "从其他账户存入"
+ },
+ "depositShapeShift": {
+ "message": "从 ShapeShift 存入"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "如果你拥有其他加密货币,你可以直接交易并存入 Ether 到你的 MetaMask 钱包。 不需要帐户。"
+ },
+ "details": {
+ "message": "详情"
+ },
+ "directDeposit": {
+ "message": "直接存入"
+ },
+ "directDepositEther": {
+ "message": "直接存入 Ether"
+ },
+ "directDepositEtherExplainer": {
+ "message": "如果你已经有了一些 Ether,通过直接转入是你的新钱包获取 Ether 的最快捷方式。"
+ },
+ "done": {
+ "message": "完成"
+ },
+ "edit": {
+ "message": "编辑"
+ },
+ "editAccountName": {
+ "message": "编辑账户名称"
+ },
+ "encryptNewDen": {
+ "message": "加密你的新 DEN"
+ },
+ "enterPassword": {
+ "message": "请输入密码"
+ },
+ "etherscanView": {
+ "message": "在 Etherscan 上查看账户"
+ },
+ "exchangeRate": {
+ "message": "兑换比率"
+ },
+ "exportPrivateKey": {
+ "message": "导出私钥"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "导出私钥需要你自担风险"
+ },
+ "failed": {
+ "message": "失败"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Exchange type"
+ },
+ "fileImportFail": {
+ "message": "文件导入失败? 点击这里!",
+ "description": "Helps user import their account from a JSON file"
+ },
+ "from": {
+ "message": "来自"
+ },
+ "fromShapeShift": {
+ "message": "来自 ShapeShift"
+ },
+ "gas": {
+ "message": "Gas",
+ "description": "Short indication of gas cost"
+ },
+ "gasFee": {
+ "message": "Gas 费用"
+ },
+ "gasLimit": {
+ "message": "Gas Limit"
+ },
+ "gasLimitCalculation": {
+ "message": "我们根据网络成功率计算建议的 Gas Limit。"
+ },
+ "gasLimitRequired": {
+ "message": "Gas Limit 必填"
+ },
+ "gasLimitTooLow": {
+ "message": "Gas Limit 至少要 21000"
+ },
+ "gasPrice": {
+ "message": "Gas Price (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "我们根据网络成功率计算建议的 Gas Price"
+ },
+ "gasPriceRequired": {
+ "message": "Gas Price 必填"
+ },
+ "getEther": {
+ "message": "获取 Ether"
+ },
+ "getEtherFromFaucet": {
+ "message": "从水管获取$1网络的 Ether",
+ "description": "Displays network name for Ether faucet"
+ },
+ "greaterThanMin": {
+ "message": "必须要大于等于 $1。",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "here": {
+ "message": "这里",
+ "description": "as in -click here- for more information (goes with troubleTokenBalances)"
+ },
+ "hide": {
+ "message": "隐藏"
+ },
+ "hideToken": {
+ "message": "隐藏代币"
+ },
+ "hideTokenPrompt": {
+ "message": "隐藏代币?"
+ },
+ "howToDeposit": {
+ "message": "你想怎样转入 Ether?"
+ },
+ "import": {
+ "message": "导入",
+ "description": "Button to import an account from a selected file"
+ },
+ "importAccount": {
+ "message": "导入账户"
+ },
+ "importAnAccount": {
+ "message": "导入一个账户"
+ },
+ "importDen": {
+ "message": "导入存在的 DEN"
+ },
+ "imported": {
+ "message": "已导入私钥",
+ "description": "status showing that an account has been fully loaded into the keyring"
+ },
+ "infoHelp": {
+ "message": "信息 & 帮助"
+ },
+ "invalidAddress": {
+ "message": "错误的地址"
+ },
+ "invalidGasParams": {
+ "message": "错误的 Gas 参数"
+ },
+ "invalidInput": {
+ "message": "错误的输入。"
+ },
+ "invalidRequest": {
+ "message": "无效请求"
+ },
+ "jsonFile": {
+ "message": "JSON 文件",
+ "description": "format for importing an account"
+ },
+ "kovan": {
+ "message": "Kovan 测试网络"
+ },
+ "lessThanMax": {
+ "message": "必须小于等于 $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "limit": {
+ "message": "限定"
+ },
+ "loading": {
+ "message": "加载..."
+ },
+ "loadingTokens": {
+ "message": "加载代币..."
+ },
+ "localhost": {
+ "message": "本地主机 8545"
+ },
+ "logout": {
+ "message": "登出"
+ },
+ "loose": {
+ "message": "疏松"
+ },
+ "mainnet": {
+ "message": "以太坊主网络"
+ },
+ "message": {
+ "message": "消息"
+ },
+ "min": {
+ "message": "最小"
+ },
+ "myAccounts": {
+ "message": "我的账户"
+ },
+ "needEtherInWallet": {
+ "message": "使用 MetaMask 与 DAPP 交互,需要你的钱包里有 Ether。"
+ },
+ "needImportFile": {
+ "message": "必须选择导入一个文件。",
+ "description": "User is important an account and needs to add a file to continue"
+ },
+ "needImportPassword": {
+ "message": "必须为已选择的文件输入密码。",
+ "description": "Password and file needed to import an account"
+ },
+ "networks": {
+ "message": "网络"
+ },
+ "newAccount": {
+ "message": "新账户"
+ },
+ "newAccountNumberName": {
+ "message": "账户 $1",
+ "description": "Default name of next account to be created on create account screen"
+ },
+ "newContract": {
+ "message": "新合约"
+ },
+ "newPassword": {
+ "message": "新密码(至少 8 个字符)"
+ },
+ "newRecipient": {
+ "message": "新收款人"
+ },
+ "next": {
+ "message": "下一个"
+ },
+ "noAddressForName": {
+ "message": "此 ENS 名字还没有指定地址。"
+ },
+ "noDeposits": {
+ "message": "没有已收的存款"
+ },
+ "noTransactionHistory": {
+ "message": "没有交易历史。"
+ },
+ "noTransactions": {
+ "message": "没有交易"
+ },
+ "notStarted": {
+ "message": "未开始"
+ },
+ "oldUI": {
+ "message": "旧版界面"
+ },
+ "oldUIMessage": {
+ "message": "你已经切换到旧版界面。 你可以通过右上方下拉菜单中的选项切换回新的用户界面。"
+ },
+ "or": {
+ "message": "或",
+ "description": "choice between creating or importing a new account"
+ },
+ "passwordMismatch": {
+ "message": "密码不匹配",
+ "description": "in password creation process, the two new password fields did not match"
+ },
+ "passwordShort": {
+ "message": "密码不够长",
+ "description": "in password creation process, the password is not long enough to be secure"
+ },
+ "pastePrivateKey": {
+ "message": "请粘贴你的私钥:",
+ "description": "For importing an account from a private key"
+ },
+ "pasteSeed": {
+ "message": "请粘贴你的助记词!"
+ },
+ "pleaseReviewTransaction": {
+ "message": "请检查你的交易。"
+ },
+ "privateKey": {
+ "message": "私钥",
+ "description": "select this type of file to use to import an account"
+ },
+ "privateKeyWarning": {
+ "message": "注意:永远不要公开这个私钥。任何拥有你的私钥的人都可以窃取你帐户中的任何资产。"
+ },
+ "privateNetwork": {
+ "message": "私有网络"
+ },
+ "qrCode": {
+ "message": "显示二维码"
+ },
+ "readdToken": {
+ "message": "之后你还可以通过帐户选项菜单中的“添加代币”来添加此代币。"
+ },
+ "readMore": {
+ "message": "了解更多。"
+ },
+ "receive": {
+ "message": "接收"
+ },
+ "recipientAddress": {
+ "message": "接收地址"
+ },
+ "refundAddress": {
+ "message": "你的退款地址"
+ },
+ "rejected": {
+ "message": "拒绝"
+ },
+ "required": {
+ "message": "必填"
+ },
+ "retryWithMoreGas": {
+ "message": "使用更高的 Gas Price 重试"
+ },
+ "revert": {
+ "message": "还原"
+ },
+ "rinkeby": {
+ "message": "Rinkeby 测试网络"
+ },
+ "ropsten": {
+ "message": "Ropsten 测试网络"
+ },
+ "sampleAccountName": {
+ "message": "例如:我的账户",
+ "description": "Help user understand concept of adding a human-readable name to their account"
+ },
+ "save": {
+ "message": "保存"
+ },
+ "saveAsFile": {
+ "message": "保存文件",
+ "description": "Account export process"
+ },
+ "selectService": {
+ "message": "选择服务"
+ },
+ "send": {
+ "message": "发送"
+ },
+ "sendTokens": {
+ "message": "发送代币"
+ },
+ "sendTokensAnywhere": {
+ "message": "发送代币给拥有以太坊账户的任何人"
+ },
+ "settings": {
+ "message": "设置"
+ },
+ "shapeshiftBuy": {
+ "message": "使用 Shapeshift 购买"
+ },
+ "showPrivateKeys": {
+ "message": "显示私钥"
+ },
+ "showQRCode": {
+ "message": "显示二维码"
+ },
+ "sign": {
+ "message": "签名"
+ },
+ "signMessage": {
+ "message": "签署消息"
+ },
+ "signNotice": {
+ "message": "签署此消息可能会产生危险的副作用。 \n只从你完全信任的网站上签名。 这种危险的方法将在未来的版本中被移除。"
+ },
+ "sigRequest": {
+ "message": "请求签名"
+ },
+ "sigRequested": {
+ "message": "签名已请求"
+ },
+ "status": {
+ "message": "状态"
+ },
+ "submit": {
+ "message": "提交"
+ },
+ "takesTooLong": {
+ "message": "花费太长时间?"
+ },
+ "testFaucet": {
+ "message": "测试水管"
+ },
+ "to": {
+ "message": "至"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 ETH 通过 ShapeShift",
+ "description": "system will fill in deposit type in start of message"
+ },
+ "tokenBalance": {
+ "message": "代币余额:"
+ },
+ "total": {
+ "message": "总量"
+ },
+ "transactionMemo": {
+ "message": "交易备注 (可选)"
+ },
+ "transactionNumber": {
+ "message": "交易号"
+ },
+ "transfers": {
+ "message": "Transfers"
+ },
+ "troubleTokenBalances": {
+ "message": "无法加载代币余额。你可以再这里查看 ",
+ "description": "Followed by a link (here) to view token balances"
+ },
+ "typePassword": {
+ "message": "请输入密码"
+ },
+ "uiWelcome": {
+ "message": "欢迎使用新版界面 (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "你现在正在使用新的 Metamask 界面。 尝试发送代币等新功能,有任何问题请告知我们。"
+ },
+ "unavailable": {
+ "message": "不可用"
+ },
+ "unknown": {
+ "message": "未知"
+ },
+ "unknownNetwork": {
+ "message": "未知私有网络"
+ },
+ "unknownNetworkId": {
+ "message": "未知网络 ID"
+ },
+ "usaOnly": {
+ "message": "只限于美国",
+ "description": "Using this exchange is limited to people inside the USA"
+ },
+ "usedByClients": {
+ "message": "可用于各种不同的客户端"
+ },
+ "viewAccount": {
+ "message": "查看账户"
+ },
+ "warning": {
+ "message": "警告"
+ },
+ "whatsThis": {
+ "message": "这是什么?"
+ },
+ "yourSigRequested": {
+ "message": "正在请求你的签名"
+ },
+ "youSign": {
+ "message": "正在签名"
}
}
diff --git a/app/images/arrow-right.svg b/app/images/arrow-right.svg
new file mode 100644
index 000000000..ea5cd0609
--- /dev/null
+++ b/app/images/arrow-right.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="25px" height="21px" viewBox="0 0 25 21" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
+ <title>arrow-right</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Confirm-Send-ETH---V3" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(-500.000000, -235.000000)">
+ <g id="Group-4" transform="translate(312.000000, 99.000000)" fill="#5B5B5B">
+ <g id="Group-18" transform="translate(109.000000, 119.000000)">
+ <g id="arrow-right" transform="translate(78.000000, 17.000000)">
+ <path d="M13.2809946,0.467462271 L13.2809946,0.467462271 C12.6477116,1.09191648 12.6477116,2.10286154 13.2809946,2.72571868 L19.1052554,8.46878095 L2.61883998,8.46878095 C1.72479329,8.46878095 0.999190175,9.18426813 0.999190175,10.0658505 L0.999190175,10.0674476 C0.999190175,10.94903 1.72479329,11.6629201 2.61883998,11.6629201 L19.1052554,11.6629201 L13.2809946,17.4075795 C12.6477116,18.0304366 12.6477116,19.0413817 13.2809946,19.6658359 C13.9126581,20.288693 14.9378964,20.288693 15.5711795,19.6658359 L25.3052748,10.0658505 L15.5711795,0.467462271 C14.9378964,-0.155394872 13.9126581,-0.155394872 13.2809946,0.467462271" id="Fill-1"></path>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/app/images/deposit-eth.svg b/app/images/deposit-eth.svg
new file mode 100644
index 000000000..a2c69242e
--- /dev/null
+++ b/app/images/deposit-eth.svg
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="80px" height="78px" viewBox="0 0 80 78" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <!-- Generator: Sketch 49 (51002) - http://www.bohemiancoding.com/sketch -->
+ <title>deposit-eth</title>
+ <desc>Created with Sketch.</desc>
+ <defs>
+ <circle id="path-1" cx="34" cy="34" r="34"></circle>
+ <circle id="path-2" cx="17" cy="17" r="17"></circle>
+ </defs>
+ <g id="Buy-ETH" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+ <g id="deposit-Ether" transform="translate(-79.000000, -146.000000)">
+ <g id="Group-12" transform="translate(8.000000, 10.000000)">
+ <g id="Group-7" transform="translate(71.000000, 122.000000)">
+ <g id="deposit-eth" transform="translate(0.000000, 14.000000)">
+ <g id="Oval">
+ <use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-1"></use>
+ <circle stroke="#38393A" stroke-width="3" cx="34" cy="34" r="32.5"></circle>
+ </g>
+ <path d="M34.406509,44.95 L22,37.7 L34.406509,55 L46.8205983,37.7 L34.4039822,44.95 L34.406509,44.95 Z M34.593491,15 L22.186982,35.37 L34.593491,42.6275 L47,35.3775 L34.593491,15 Z" id="Shape" fill="#38393A"></path>
+ <g id="Group-6" transform="translate(46.000000, 44.000000)">
+ <g id="Oval-Copy">
+ <use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-2"></use>
+ <circle stroke="#38393A" stroke-width="3" cx="17" cy="17" r="15.5"></circle>
+ </g>
+ <path d="M19.0769231,15.4230769 L25.5,15.4230769 L25.5,19.5769231 L19.0769231,19.5769231 L19.0769231,26 L14.9230769,26 L14.9230769,19.5769231 L8.5,19.5769231 L8.5,15.4230769 L14.9230769,15.4230769 L14.9230769,9 L19.0769231,9 L19.0769231,15.4230769 Z" id="Combined-Shape" fill="#38393A"></path>
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/app/manifest.json b/app/manifest.json
index 0c89c2b3e..6fcf6cd7c 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,10 +1,10 @@
{
- "name": "MetaMask",
- "short_name": "Metamask",
+ "name": "__MSG_appName__",
+ "short_name": "__MSG_appName__",
"version": "4.2.0",
"manifest_version": 2,
"author": "https://metamask.io",
- "description": "Ethereum Browser Extension",
+ "description": "__MSG_appDescription__",
"commands": {
"_execute_browser_action": {
"suggested_key": {
@@ -56,6 +56,7 @@
],
"permissions": [
"storage",
+ "unlimitedStorage",
"clipboardWrite",
"http://localhost:8545/",
"https://*.infura.io/"
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 601ae0372..ef5513ec7 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -1,9 +1,11 @@
const urlUtil = require('url')
const endOfStream = require('end-of-stream')
const pump = require('pump')
+const debounce = require('debounce-stream')
const log = require('loglevel')
const extension = require('extensionizer')
const LocalStorageStore = require('obs-store/lib/localStorage')
+const LocalStore = require('./lib/local-store')
const storeTransform = require('obs-store/lib/transform')
const asStream = require('obs-store/lib/asStream')
const ExtensionPlatform = require('./platforms/extension')
@@ -44,6 +46,8 @@ let openMetamaskTabsIDs = {}
// state persistence
const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY })
+const localStore = new LocalStore()
+let versionedData
// initialization flow
initialize().catch(log.error)
@@ -64,12 +68,23 @@ async function initialize () {
async function loadStateFromPersistence () {
// migrations
const migrator = new Migrator({ migrations })
+
// read from disk
- let versionedData = diskStore.getState() || migrator.generateInitialState(firstTimeState)
+ // first from preferred, async API:
+ versionedData = (await localStore.get()) ||
+ diskStore.getState() ||
+ migrator.generateInitialState(firstTimeState)
+
// migrate data
versionedData = await migrator.migrateData(versionedData)
+ if (!versionedData) {
+ throw new Error('MetaMask - migrator returned undefined')
+ }
+
// write to disk
+ if (localStore.isSupported) localStore.set(versionedData)
diskStore.putState(versionedData)
+
// return just the data
return versionedData.data
}
@@ -102,16 +117,30 @@ function setupController (initState) {
// setup state persistence
pump(
asStream(controller.store),
+ debounce(1000),
storeTransform(versionifyData),
- asStream(diskStore)
+ storeTransform(syncDataWithExtension),
+ asStream(diskStore),
+ (error) => {
+ log.error('pump hit error', error)
+ }
)
function versionifyData (state) {
- const versionedData = diskStore.getState()
versionedData.data = state
return versionedData
}
+ function syncDataWithExtension(state) {
+ if (localStore.isSupported) {
+ localStore.set(state)
+ .catch((err) => {
+ log.error('error setting state in local store:', err)
+ })
+ }
+ return state
+ }
+
//
// connect to other contexts
//
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index 2ed7c87b6..7abbc60e7 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -96,7 +96,8 @@ function logStreamDisconnectWarning (remoteLabel, err) {
}
function shouldInjectWeb3 () {
- return doctypeCheck() && suffixCheck() && documentElementCheck()
+ return doctypeCheck() && suffixCheck()
+ && documentElementCheck() && !blacklistedDomainCheck()
}
function doctypeCheck () {
@@ -129,6 +130,20 @@ function documentElementCheck () {
return true
}
+function blacklistedDomainCheck () {
+ var blacklistedDomains = ['uscourts.gov', 'dropbox.com']
+ var currentUrl = window.location.href
+ var currentRegex
+ for (let i = 0; i < blacklistedDomains.length; i++) {
+ const blacklistedDomain = blacklistedDomains[i].replace('.', '\\.')
+ currentRegex = new RegExp(`(?:https?:\\/\\/)(?:(?!${blacklistedDomain}).)*$`)
+ if (!currentRegex.test(currentUrl)) {
+ return true
+ }
+ }
+ return false
+}
+
function redirectToPhishingWarning () {
console.log('MetaMask - redirecting to phishing warning')
window.location.href = 'https://metamask.io/phishing.html'
diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js
index 9c2ca0dc8..3e3909361 100644
--- a/app/scripts/controllers/transactions.js
+++ b/app/scripts/controllers/transactions.js
@@ -6,7 +6,6 @@ const EthQuery = require('ethjs-query')
const TransactionStateManager = require('../lib/tx-state-manager')
const TxGasUtil = require('../lib/tx-gas-utils')
const PendingTransactionTracker = require('../lib/pending-tx-tracker')
-const createId = require('../lib/random-id')
const NonceTracker = require('../lib/nonce-tracker')
/*
@@ -92,8 +91,8 @@ module.exports = class TransactionController extends EventEmitter {
this.pendingTxTracker.on('tx:warning', (txMeta) => {
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:warning')
})
+ this.pendingTxTracker.on('tx:confirmed', (txId) => this._markNonceDuplicatesDropped(txId))
this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager))
- this.pendingTxTracker.on('tx:confirmed', this.txStateManager.setTxStatusConfirmed.bind(this.txStateManager))
this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => {
if (!txMeta.firstRetryBlockNumber) {
txMeta.firstRetryBlockNumber = latestBlockNumber
@@ -186,14 +185,7 @@ module.exports = class TransactionController extends EventEmitter {
// validate
await this.txGasUtil.validateTxParams(txParams)
// construct txMeta
- const txMeta = {
- id: createId(),
- time: (new Date()).getTime(),
- status: 'unapproved',
- metamaskNetworkId: this.getNetwork(),
- txParams: txParams,
- loadingDefaults: true,
- }
+ const txMeta = this.txStateManager.generateTxMeta({txParams})
this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta)
// add default tx params
@@ -215,7 +207,6 @@ module.exports = class TransactionController extends EventEmitter {
const txParams = txMeta.txParams
// ensure value
txMeta.gasPriceSpecified = Boolean(txParams.gasPrice)
- txMeta.nonceSpecified = Boolean(txParams.nonce)
let gasPrice = txParams.gasPrice
if (!gasPrice) {
gasPrice = this.getGasPrice ? this.getGasPrice() : await this.query.gasPrice()
@@ -226,11 +217,17 @@ module.exports = class TransactionController extends EventEmitter {
return await this.txGasUtil.analyzeGasUsage(txMeta)
}
- async retryTransaction (txId) {
- this.txStateManager.setTxStatusUnapproved(txId)
- const txMeta = this.txStateManager.getTx(txId)
- txMeta.lastGasPrice = txMeta.txParams.gasPrice
- this.txStateManager.updateTx(txMeta, 'retryTransaction: manual retry')
+ async retryTransaction (originalTxId) {
+ const originalTxMeta = this.txStateManager.getTx(originalTxId)
+ const lastGasPrice = originalTxMeta.txParams.gasPrice
+ const txMeta = this.txStateManager.generateTxMeta({
+ txParams: originalTxMeta.txParams,
+ lastGasPrice,
+ loadingDefaults: false,
+ })
+ this.addTx(txMeta)
+ this.emit('newUnapprovedTx', txMeta)
+ return txMeta
}
async updateTransaction (txMeta) {
@@ -253,11 +250,9 @@ module.exports = class TransactionController extends EventEmitter {
// wait for a nonce
nonceLock = await this.nonceTracker.getNonceLock(fromAddress)
// add nonce to txParams
- const nonce = txMeta.nonceSpecified ? txMeta.txParams.nonce : nonceLock.nextNonce
- if (nonce > nonceLock.nextNonce) {
- const message = `Specified nonce may not be larger than account's next valid nonce.`
- throw new Error(message)
- }
+ // if txMeta has lastGasPrice then it is a retry at same nonce with higher
+ // gas price transaction and their for the nonce should not be calculated
+ const nonce = txMeta.lastGasPrice ? txMeta.txParams.nonce : nonceLock.nextNonce
txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16))
// add nonce debugging information to txMeta
txMeta.nonceDetails = nonceLock.nonceDetails
@@ -314,6 +309,22 @@ module.exports = class TransactionController extends EventEmitter {
// PRIVATE METHODS
//
+ _markNonceDuplicatesDropped (txId) {
+ this.txStateManager.setTxStatusConfirmed(txId)
+ // get the confirmed transactions nonce and from address
+ const txMeta = this.txStateManager.getTx(txId)
+ const { nonce, from } = txMeta.txParams
+ const sameNonceTxs = this.txStateManager.getFilteredTxList({nonce, from})
+ if (!sameNonceTxs.length) return
+ // mark all same nonce transactions as dropped and give i a replacedBy hash
+ sameNonceTxs.forEach((otherTxMeta) => {
+ if (otherTxMeta.id === txId) return
+ otherTxMeta.replacedBy = txMeta.hash
+ this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:confirmed reference to confirmed txHash with same nonce')
+ this.txStateManager.setTxStatusDropped(otherTxMeta.id)
+ })
+ }
+
_updateMemstore () {
const unapprovedTxs = this.txStateManager.getUnapprovedTxList()
const selectedAddressTxList = this.txStateManager.getFilteredTxList({
diff --git a/app/scripts/lib/local-store.js b/app/scripts/lib/local-store.js
new file mode 100644
index 000000000..5b47985f6
--- /dev/null
+++ b/app/scripts/lib/local-store.js
@@ -0,0 +1,62 @@
+// We should not rely on local storage in an extension!
+// We should use this instead!
+// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage/local
+
+const extension = require('extensionizer')
+
+module.exports = class ExtensionStore {
+ constructor() {
+ this.isSupported = !!(extension.storage.local)
+ if (!this.isSupported) {
+ log.error('Storage local API not available.')
+ }
+ }
+
+ async get() {
+ if (!this.isSupported) return undefined
+ const result = await this._get()
+ // extension.storage.local always returns an obj
+ // if the object is empty, treat it as undefined
+ if (isEmpty(result)) {
+ return undefined
+ } else {
+ return result
+ }
+ }
+
+ async set(state) {
+ return this._set(state)
+ }
+
+ _get() {
+ const local = extension.storage.local
+ return new Promise((resolve, reject) => {
+ local.get(null, (result) => {
+ const err = extension.runtime.lastError
+ if (err) {
+ reject(err)
+ } else {
+ resolve(result)
+ }
+ })
+ })
+ }
+
+ _set(obj) {
+ const local = extension.storage.local
+ return new Promise((resolve, reject) => {
+ local.set(obj, () => {
+ const err = extension.runtime.lastError
+ if (err) {
+ reject(err)
+ } else {
+ resolve()
+ }
+ })
+ })
+ }
+}
+
+function isEmpty(obj) {
+ return Object.keys(obj).length === 0
+}
diff --git a/app/scripts/lib/tx-gas-utils.js b/app/scripts/lib/tx-gas-utils.js
index 6f6ff7852..0fa9dd8d4 100644
--- a/app/scripts/lib/tx-gas-utils.js
+++ b/app/scripts/lib/tx-gas-utils.js
@@ -4,7 +4,7 @@ const {
BnMultiplyByFraction,
bnToHex,
} = require('./util')
-const addHexPrefix = require('ethereumjs-util').addHexPrefix
+const { addHexPrefix, isValidAddress } = require('ethereumjs-util')
const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
/*
@@ -113,12 +113,14 @@ module.exports = class TxGasUtil {
}
}
validateRecipient (txParams) {
- if (txParams.to === '0x') {
+ if (txParams.to === '0x' || txParams.to === null ) {
if (txParams.data) {
delete txParams.to
} else {
throw new Error('Invalid recipient address')
}
+ } else if ( txParams.to !== undefined && !isValidAddress(txParams.to) ) {
+ throw new Error('Invalid recipient address')
}
return txParams
}
diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js
index 2eb006380..ad07c813f 100644
--- a/app/scripts/lib/tx-state-manager.js
+++ b/app/scripts/lib/tx-state-manager.js
@@ -1,9 +1,21 @@
const extend = require('xtend')
const EventEmitter = require('events')
const ObservableStore = require('obs-store')
+const createId = require('./random-id')
const ethUtil = require('ethereumjs-util')
const txStateHistoryHelper = require('./tx-state-history-helper')
+// STATUS METHODS
+ // statuses:
+ // - `'unapproved'` the user has not responded
+ // - `'rejected'` the user has responded no!
+ // - `'approved'` the user has approved the tx
+ // - `'signed'` the tx is signed
+ // - `'submitted'` the tx is sent to a server
+ // - `'confirmed'` the tx has been included in a block.
+ // - `'failed'` the tx failed for some reason, included on tx data.
+ // - `'dropped'` the tx nonce was already used
+
module.exports = class TransactionStateManager extends EventEmitter {
constructor ({ initState, txHistoryLimit, getNetwork }) {
super()
@@ -16,6 +28,16 @@ module.exports = class TransactionStateManager extends EventEmitter {
this.getNetwork = getNetwork
}
+ generateTxMeta (opts) {
+ return extend({
+ id: createId(),
+ time: (new Date()).getTime(),
+ status: 'unapproved',
+ metamaskNetworkId: this.getNetwork(),
+ loadingDefaults: true,
+ }, opts)
+ }
+
// Returns the number of txs for the current network.
getTxCount () {
return this.getTxList().length
@@ -164,16 +186,6 @@ module.exports = class TransactionStateManager extends EventEmitter {
})
}
- // STATUS METHODS
- // statuses:
- // - `'unapproved'` the user has not responded
- // - `'rejected'` the user has responded no!
- // - `'approved'` the user has approved the tx
- // - `'signed'` the tx is signed
- // - `'submitted'` the tx is sent to a server
- // - `'confirmed'` the tx has been included in a block.
- // - `'failed'` the tx failed for some reason, included on tx data.
-
// get::set status
// should return the status of the tx.
@@ -202,7 +214,11 @@ module.exports = class TransactionStateManager extends EventEmitter {
}
// should update the status of the tx to 'submitted'.
+ // and add a time stamp for when it was called
setTxStatusSubmitted (txId) {
+ const txMeta = this.getTx(txId)
+ txMeta.submittedTime = (new Date()).getTime()
+ this.updateTx(txMeta, 'txStateManager - add submitted time stamp')
this._setTxStatus(txId, 'submitted')
}
@@ -211,6 +227,12 @@ module.exports = class TransactionStateManager extends EventEmitter {
this._setTxStatus(txId, 'confirmed')
}
+ // should update the status dropped
+ setTxStatusDropped (txId) {
+ this._setTxStatus(txId, 'dropped')
+ }
+
+
setTxStatusFailed (txId, err) {
const txMeta = this.getTx(txId)
txMeta.err = {
diff --git a/development/genStates.js b/development/genStates.js
index 39a672ee0..bc274c757 100644
--- a/development/genStates.js
+++ b/development/genStates.js
@@ -1,18 +1,21 @@
const fs = require('fs')
const path = require('path')
+const promisify = require('pify')
-const statesPath = path.join(__dirname, 'states')
-const stateNames = fs.readdirSync(statesPath)
+start().catch(console.error)
-const states = stateNames.reduce((result, stateFileName) => {
- const statePath = path.join(__dirname, 'states', stateFileName)
- const stateFile = fs.readFileSync(statePath).toString()
- const state = JSON.parse(stateFile)
- result[stateFileName.split('.')[0].replace(/-/g, ' ', 'g')] = state
- return result
-}, {})
-
-const result = `module.exports = ${JSON.stringify(states)}`
-
-const statesJsonPath = path.join(__dirname, 'states.js')
-fs.writeFileSync(statesJsonPath, result)
+async function start () {
+ const statesPath = path.join(__dirname, 'states')
+ const stateFilesNames = await promisify(fs.readdir)(statesPath)
+ const states = {}
+ await Promise.all(stateFilesNames.map(async (stateFileName) => {
+ const stateFilePath = path.join(__dirname, 'states', stateFileName)
+ const stateFileContent = await promisify(fs.readFile)(stateFilePath, 'utf8')
+ const state = JSON.parse(stateFileContent)
+ const stateName = stateFileName.split('.')[0].replace(/-/g, ' ', 'g')
+ states[stateName] = state
+ }))
+ const generatedFileContent = `module.exports = ${JSON.stringify(states)}`
+ const generatedFilePath = path.join(__dirname, 'states.js')
+ await promisify(fs.writeFile)(generatedFilePath, generatedFileContent)
+}
diff --git a/mock-dev.js b/development/mock-dev.js
index 6627c1153..a1fb3a86d 100644
--- a/mock-dev.js
+++ b/development/mock-dev.js
@@ -15,16 +15,16 @@
const extend = require('xtend')
const render = require('react-dom').render
const h = require('react-hyperscript')
-const Root = require('./ui/app/root')
-const configureStore = require('./ui/app/store')
-const actions = require('./ui/app/actions')
-const states = require('./development/states')
-const backGroundConnectionModifiers = require('./development/backGroundConnectionModifiers')
-const Selector = require('./development/selector')
-const MetamaskController = require('./app/scripts/metamask-controller')
-const firstTimeState = require('./app/scripts/first-time-state')
-const ExtensionPlatform = require('./app/scripts/platforms/extension')
-const extension = require('./development/mockExtension')
+const Root = require('../ui/app/root')
+const configureStore = require('../ui/app/store')
+const actions = require('../ui/app/actions')
+const states = require('./states')
+const backGroundConnectionModifiers = require('./backGroundConnectionModifiers')
+const Selector = require('./selector')
+const MetamaskController = require('../app/scripts/metamask-controller')
+const firstTimeState = require('../app/scripts/first-time-state')
+const ExtensionPlatform = require('../app/scripts/platforms/extension')
+const extension = require('./mockExtension')
const noop = function () {}
const log = require('loglevel')
@@ -51,7 +51,7 @@ function updateQueryParams(newView) {
// CSS
//
-const MetaMaskUiCss = require('./ui/css')
+const MetaMaskUiCss = require('../ui/css')
const injectCss = require('inject-css')
//
diff --git a/development/run-version-bump.js b/development/run-version-bump.js
index fde14566e..98757f58e 100644
--- a/development/run-version-bump.js
+++ b/development/run-version-bump.js
@@ -1,4 +1,4 @@
-const { promisify } = require('util')
+const promisify = require('pify')
const fs = require('fs')
const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile)
diff --git a/development/states/confirm-new-ui.json b/development/states/confirm-new-ui.json
index 6ea8e64cd..6981781a9 100644
--- a/development/states/confirm-new-ui.json
+++ b/development/states/confirm-new-ui.json
@@ -116,7 +116,7 @@
"send": {
"gasLimit": "0xea60",
"gasPrice": "0xba43b7400",
- "gasTotal": "0xb451dc41b578",
+ "gasTotal": "0xaa87bee538000",
"tokenBalance": null,
"from": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
diff --git a/development/states/send-edit.json b/development/states/send-edit.json
index 6ea8e64cd..6981781a9 100644
--- a/development/states/send-edit.json
+++ b/development/states/send-edit.json
@@ -116,7 +116,7 @@
"send": {
"gasLimit": "0xea60",
"gasPrice": "0xba43b7400",
- "gasTotal": "0xb451dc41b578",
+ "gasTotal": "0xaa87bee538000",
"tokenBalance": null,
"from": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
diff --git a/ui-dev.js b/development/ui-dev.js
index 620d81667..cac433909 100644
--- a/ui-dev.js
+++ b/development/ui-dev.js
@@ -17,10 +17,10 @@
const render = require('react-dom').render
const h = require('react-hyperscript')
-const Root = require('./ui/app/root')
-const configureStore = require('./development/uiStore')
-const states = require('./development/states')
-const Selector = require('./development/selector')
+const Root = require('../ui/app/root')
+const configureStore = require('./uiStore')
+const states = require('./states')
+const Selector = require('./selector')
// logger
const log = require('loglevel')
@@ -35,7 +35,7 @@ const firstState = states[selectedView]
updateQueryParams(selectedView)
// CSS
-const MetaMaskUiCss = require('./ui/css')
+const MetaMaskUiCss = require('../ui/css')
const injectCss = require('inject-css')
diff --git a/development/verify-locale-strings.js b/development/verify-locale-strings.js
new file mode 100644
index 000000000..b8fe5a7dc
--- /dev/null
+++ b/development/verify-locale-strings.js
@@ -0,0 +1,96 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Locale verification script
+//
+// usage:
+//
+// node app/scripts/verify-locale-strings.js <locale>
+//
+// will check the given locale against the strings in english
+//
+////////////////////////////////////////////////////////////////////////////////
+
+var fs = require('fs')
+var path = require('path')
+
+console.log('Locale Verification')
+
+var locale = process.argv[2]
+if (!locale || locale == '') {
+ console.log('Must enter a locale as argument. exitting')
+ process.exit(1)
+}
+
+console.log("verifying for locale " + locale)
+
+localeFilePath = path.join(process.cwd(), 'app', '_locales', locale, 'messages.json')
+try {
+ localeObj = JSON.parse(fs.readFileSync(localeFilePath, 'utf8'));
+} catch (e) {
+ if(e.code == 'ENOENT') {
+ console.log('Locale file not found')
+ } else {
+ console.log('Error opening your locale file: ', e)
+ }
+ process.exit(1)
+}
+
+englishFilePath = path.join(process.cwd(), 'app', '_locales', 'en', 'messages.json')
+try {
+ englishObj = JSON.parse(fs.readFileSync(englishFilePath, 'utf8'));
+} catch (e) {
+ if(e.code == 'ENOENT') {
+ console.log("English File not found")
+ } else {
+ console.log("Error opening english locale file: ", e)
+ }
+ process.exit(1)
+}
+
+console.log('\tverifying whether all your locale strings are contained in the english one')
+
+var counter = 0
+var foundErrorA = false
+var notFound = [];
+Object.keys(localeObj).forEach(function(key){
+ if (!englishObj[key]) {
+ foundErrorA = true
+ notFound.push(key)
+ }
+ counter++
+})
+
+if (foundErrorA) {
+ console.log('\nThe following string(s) is(are) not found in the english locale:')
+ notFound.forEach(function(key) {
+ console.log(key)
+ })
+} else {
+ console.log('\tall ' + counter +' strings declared in your locale were found in the english one')
+}
+
+console.log('\n\tverifying whether your locale contains all english strings')
+
+var counter = 0
+var foundErrorB = false
+var notFound = [];
+Object.keys(englishObj).forEach(function(key){
+ if (!localeObj[key]) {
+ foundErrorB = true
+ notFound.push(key)
+ }
+ counter++
+})
+
+if (foundErrorB) {
+ console.log('\nThe following string(s) is(are) not found in the your locale:')
+ notFound.forEach(function(key) {
+ console.log(key)
+ })
+} else {
+ console.log('\tall ' + counter +' english strings were found in your locale!')
+}
+
+if (!foundErrorA && !foundErrorB) {
+ console.log('You are good to go')
+} \ No newline at end of file
diff --git a/docs/add-to-chrome.md b/docs/add-to-chrome.md
index ea5213182..9c321a1a6 100644
--- a/docs/add-to-chrome.md
+++ b/docs/add-to-chrome.md
@@ -1,14 +1,12 @@
## Add Custom Build to Chrome
-Open `Settings` > `Extensions`.
-
-Check "Developer mode".
-
-At the top, click `Load Unpacked Extension`.
-
-Navigate to your `metamask-plugin/dist/chrome` folder.
-
-Click `Select`.
+* Open `Settings` > `Extensions`.
+* Check "Developer mode".
+* Alternatively, use the URL `chrome://extensions/` in your address bar
+* At the top, click `Load Unpacked Extension`.
+* Navigate to your `metamask-plugin/dist/chrome` folder.
+* Click `Select`.
+* Change to your locale via `chrome://settings/languages`
+* Restart the browser and test the plugin in your locale
You now have the plugin, and can click 'inspect views: background plugin' to view its dev console.
-
diff --git a/docs/translating-guide.md b/docs/translating-guide.md
new file mode 100644
index 000000000..ae2dfecd3
--- /dev/null
+++ b/docs/translating-guide.md
@@ -0,0 +1,26 @@
+# MetaMask Translation Guide
+
+The MetaMask browser extension supports new translations added in the form of new locales files added in `app/_locales`.
+
+- [The MDN Guide to Internationalizing Extensions](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Internationalization)
+
+## Adding a new Language
+
+Each supported language is represented by a folder in `app/_locales` whose name is that language's subtag ([look up a language subtag using this tool](https://r12a.github.io/app-subtags/)).
+
+Inside that folder there should be a `messages.json` file that follows the specified format. An easy way to start your translation is to first duplicate `app/_locales/en/messages.json` (the english translation), and then update the `message` key for each in-app message.
+
+That's it! When MetaMask is loaded on a computer with that language set as the system language, they will see your translation instead of the default one.
+
+## Testing
+
+To automatically see if you are missing any phrases to translate, we have a script you can run (if you know how to use the command line). The script is:
+
+```
+node development/verify-locale-strings.js $YOUR_LOCALE
+```
+
+Where `$YOUR_LOCALE` is your [locale string](https://r12a.github.io/app-subtags/), i.e. the name of your language folder.
+
+To verify that your translation works in the app, you will need to [build a local copy](https://github.com/MetaMask/metamask-extension#building-locally) of MetaMask. You will need to change your browser language, your operating system language, and restart your browser (sorry it's so much work!).
+
diff --git a/gulpfile.js b/gulpfile.js
index adfb148a9..dbbb1e4ff 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -408,7 +408,11 @@ function bundleTask(opts) {
.pipe(gulpif(debug, sourcemaps.init({ loadMaps: true })))
// Minification
.pipe(gulpif(opts.isBuild, uglify({
- mangle: { reserved: [ 'MetamaskInpageProvider' ] },
+ mangle: { reserved: [ 'MetamaskInpageProvider' ] },
+ })))
+ // Transpile to ES5
+ .pipe(gulpif(opts.isBuild, babel({
+ presets: ['env']
})))
// writes .map file
.pipe(gulpif(debug, sourcemaps.write('./')))
diff --git a/mascara/src/app/first-time/backup-phrase-screen.js b/mascara/src/app/first-time/backup-phrase-screen.js
index 9db61f3ab..6819abcf3 100644
--- a/mascara/src/app/first-time/backup-phrase-screen.js
+++ b/mascara/src/app/first-time/backup-phrase-screen.js
@@ -78,26 +78,28 @@ class BackupPhraseScreen extends Component {
{this.props.seedWords}
</div>
{!isShowingSecret && (
- <div className="backup-phrase__secret-blocker">
+ <div
+ className="backup-phrase__secret-blocker"
+ onClick={() => this.setState({ isShowingSecret: true })}
+ >
<LockIcon width="28px" height="35px" fill="#FFFFFF" />
- <button
+ <div
className="backup-phrase__reveal-button"
- onClick={() => this.setState({ isShowingSecret: true })}
>
Click here to reveal secret words
- </button>
+ </div>
</div>
)}
</div>
- );
+ )
}
- renderSecretScreen() {
+ renderSecretScreen () {
const { isShowingSecret } = this.state
return (
<div className="backup-phrase__content-wrapper">
- <div>
+ <div className="backup-phrase__phrase">
<div className="backup-phrase__title">Secret Backup Phrase</div>
<div className="backup-phrase__body-text">
Your secret backup phrase makes it easy to back up and restore your account.
@@ -106,17 +108,6 @@ class BackupPhraseScreen extends Component {
WARNING: Never disclose your backup phrase. Anyone with this phrase can take your Ether forever.
</div>
{this.renderSecretWordsContainer()}
- <button
- className="first-time-flow__button"
- onClick={() => isShowingSecret && this.setState({
- isShowingSecret: false,
- page: BackupPhraseScreen.PAGE.CONFIRM
- })}
- disabled={!isShowingSecret}
- >
- Next
- </button>
- <Breadcrumbs total={3} currentIndex={1} />
</div>
<div className="backup-phrase__tips">
<div className="backup-phrase__tips-text">Tips:</div>
@@ -130,6 +121,19 @@ class BackupPhraseScreen extends Component {
Memorize this phrase.
</div>
</div>
+ <div className="backup-phrase__next-button">
+ <button
+ className="first-time-flow__button"
+ onClick={() => isShowingSecret && this.setState({
+ isShowingSecret: false,
+ page: BackupPhraseScreen.PAGE.CONFIRM,
+ })}
+ disabled={!isShowingSecret}
+ >
+ Next
+ </button>
+ <Breadcrumbs total={3} currentIndex={1} />
+ </div>
</div>
)
}
@@ -231,10 +235,14 @@ class BackupPhraseScreen extends Component {
return this.props.isLoading
? <LoadingScreen loadingMessage="Creating your new account" />
: (
- <div className="backup-phrase">
- {this.renderBack()}
- <Identicon address={this.props.address} diameter={70} />
- {this.renderContent()}
+ <div className="first-view-main-wrapper">
+ <div className="first-view-main">
+ <div className="backup-phrase">
+ {this.renderBack()}
+ <Identicon address={this.props.address} diameter={70} />
+ {this.renderContent()}
+ </div>
+ </div>
</div>
)
}
diff --git a/mascara/src/app/first-time/create-password-screen.js b/mascara/src/app/first-time/create-password-screen.js
index 450d6a479..192da3399 100644
--- a/mascara/src/app/first-time/create-password-screen.js
+++ b/mascara/src/app/first-time/create-password-screen.js
@@ -2,6 +2,7 @@ import EventEmitter from 'events'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
+import classnames from 'classnames'
import {createNewVaultAndKeychain} from '../../../../ui/app/actions'
import LoadingScreen from './loading-screen'
import Breadcrumbs from './breadcrumbs'
@@ -14,6 +15,7 @@ class CreatePasswordScreen extends Component {
goToImportWithSeedPhrase: PropTypes.func.isRequired,
goToImportAccount: PropTypes.func.isRequired,
next: PropTypes.func.isRequired,
+ isMascara: PropTypes.bool.isRequired,
}
state = {
@@ -53,14 +55,17 @@ class CreatePasswordScreen extends Component {
}
render () {
- const { isLoading, goToImportWithSeedPhrase } = this.props
+ const { isLoading, goToImportWithSeedPhrase, isMascara } = this.props
return isLoading
? <LoadingScreen loadingMessage="Creating your new account" />
: (
- <div>
- <div className="first-view-main">
- <div className="mascara-info">
+ <div className={classnames({ 'first-view-main-wrapper': !isMascara })}>
+ <div className={classnames({
+ 'first-view-main': !isMascara,
+ 'first-view-main__mascara': isMascara,
+ })}>
+ {isMascara && <div className="mascara-info first-view-phone-invisible">
<Mascot
animationEventEmitter={this.animationEventEmitter}
width="225"
@@ -72,7 +77,7 @@ class CreatePasswordScreen extends Component {
<div className="info">
It allows you to hold ether & tokens, and interact with decentralized applications.
</div>
- </div>
+ </div>}
<div className="create-password">
<div className="create-password__title">
Create Password
@@ -127,7 +132,7 @@ class CreatePasswordScreen extends Component {
}
export default connect(
- ({ appState: { isLoading } }) => ({ isLoading }),
+ ({ appState: { isLoading }, metamask: { isMascara } }) => ({ isLoading, isMascara }),
dispatch => ({
createAccount: password => dispatch(createNewVaultAndKeychain(password)),
})
diff --git a/mascara/src/app/first-time/import-seed-phrase-screen.js b/mascara/src/app/first-time/import-seed-phrase-screen.js
index de8d675e1..86f02ceac 100644
--- a/mascara/src/app/first-time/import-seed-phrase-screen.js
+++ b/mascara/src/app/first-time/import-seed-phrase-screen.js
@@ -79,70 +79,73 @@ class ImportSeedPhraseScreen extends Component {
const { warning } = this.props
const importDisabled = warning || !seedPhrase || !password || !confirmPassword
return (
- <div className="import-account">
- <a
- className="import-account__back-button"
- onClick={e => {
- e.preventDefault()
- this.props.back()
- }}
- href="#"
- >
- {`< Back`}
- </a>
- <div className="import-account__title">
- Import an Account with Seed Phrase
+ <div className="first-view-main-wrapper">
+ <div className="first-view-main">
+ <div className="import-account">
+ <a
+ className="import-account__back-button"
+ onClick={e => {
+ e.preventDefault()
+ this.props.back()
+ }}
+ href="#"
+ >
+ {`< Back`}
+ </a>
+ <div className="import-account__title">
+ Import an Account with Seed Phrase
+ </div>
+ <div className="import-account__selector-label">
+ Enter your secret twelve word phrase here to restore your vault.
+ </div>
+ <div className="import-account__input-wrapper">
+ <label className="import-account__input-label">Wallet Seed</label>
+ <textarea
+ className="import-account__secret-phrase"
+ onChange={e => this.onChange({seedPhrase: e.target.value})}
+ value={this.state.seedPhrase}
+ placeholder="Separate each word with a single space"
+ />
+ </div>
+ <span
+ className="error"
+ >
+ {this.props.warning}
+ </span>
+ <div className="import-account__input-wrapper">
+ <label className="import-account__input-label">New Password</label>
+ <input
+ className="first-time-flow__input"
+ type="password"
+ placeholder="New Password (min 8 characters)"
+ onChange={e => this.onChange({password: e.target.value})}
+ />
+ </div>
+ <div className="import-account__input-wrapper">
+ <label
+ className={classnames('import-account__input-label', {
+ 'import-account__input-label__disabled': password.length < 8,
+ })}
+ >Confirm Password</label>
+ <input
+ className={classnames('first-time-flow__input', {
+ 'first-time-flow__input__disabled': password.length < 8,
+ })}
+ type="password"
+ placeholder="Confirm Password"
+ onChange={e => this.onChange({confirmPassword: e.target.value})}
+ disabled={password.length < 8}
+ />
+ </div>
+ <button
+ className="first-time-flow__button"
+ onClick={() => !importDisabled && this.onClick()}
+ disabled={importDisabled}
+ >
+ Import
+ </button>
+ </div>
</div>
- <div className="import-account__selector-label">
- Enter your secret twelve word phrase here to restore your vault.
- </div>
- <div className="import-account__input-wrapper">
- <label className="import-account__input-label">Wallet Seed</label>
- <textarea
- className="import-account__secret-phrase"
- onChange={e => this.onChange({seedPhrase: e.target.value})}
- value={this.state.seedPhrase}
- placeholder="Separate each word with a single space"
- />
- </div>
- <span
- className="error"
- >
- {this.props.warning}
- </span>
- <div className="import-account__input-wrapper">
- <label className="import-account__input-label">New Password</label>
- <input
- className="first-time-flow__input"
- type="password"
- placeholder="New Password (min 8 characters)"
- onChange={e => this.onChange({password: e.target.value})}
- />
- </div>
- <div className="import-account__input-wrapper">
- <label
- className="import-account__input-label"
- className={classnames('import-account__input-label', {
- 'import-account__input-label__disabled': password.length < 8,
- })}
- >Confirm Password</label>
- <input
- className={classnames('first-time-flow__input', {
- 'first-time-flow__input__disabled': password.length < 8,
- })}
- type="password"
- placeholder="Confirm Password"
- onChange={e => this.onChange({confirmPassword: e.target.value})}
- disabled={password.length < 8}
- />
- </div>
- <button
- className="first-time-flow__button"
- onClick={() => !importDisabled && this.onClick()}
- disabled={importDisabled}
- >
- Import
- </button>
</div>
)
}
diff --git a/mascara/src/app/first-time/index.css b/mascara/src/app/first-time/index.css
index f59eb4ce1..d7b146e51 100644
--- a/mascara/src/app/first-time/index.css
+++ b/mascara/src/app/first-time/index.css
@@ -1,3 +1,10 @@
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Roboto'), local('Roboto-Regular'), url('/fonts/Roboto/Roboto-Regular.ttf') format('truetype');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
.first-time-flow {
width: 100vw;
@@ -6,21 +13,70 @@
display: flex;
justify-content: center;
flex: 1 0 auto;
+ font-weight: 400;
+ font-family: Roboto;
}
-.alpha-warning {
+.alpha-warning__container {
+ display: flex;
+ justify-content: center;
background: #f7861c;
+}
+
+.alpha-warning,
+.alpha-warning-welcome-screen {
color: #fff;
line-height: 2em;
- padding-left: 10vw;
}
-.first-view-main {
+@media screen and (min-width: 576px) {
+ .alpha-warning {
+ width: 85vw;
+ }
+}
+
+@media screen and (min-width: 769px) {
+ .alpha-warning {
+ width: 80vw;
+ }
+}
+
+@media screen and (min-width: 1281px) {
+ .alpha-warning {
+ width: 62vw;
+ }
+}
+
+.alpha-warning-welcome-screen {
+ padding-left: 0;
+ text-align: center;
+}
+
+.first-view-main-wrapper {
+ display: flex;
+ width: 100%;
+ height: 100%;
+ justify-content: center;
+ padding: 0 10px;
+}
+
+.first-view-main,
+.first-view-main__mascara {
display: flex;
- flex-direction: row-reverse;
+ flex-direction: row;
+ justify-content: flex-start;
+}
+
+.first-view-main__mascara {
justify-content: space-between;
}
+@media screen and (min-width: 1281px) {
+ .first-view-main {
+ width: 62vw;
+ }
+}
+
.mascara-info {
display: flex;
flex-flow: column;
@@ -45,12 +101,8 @@
.buy-ether {
display: flex;
flex-flow: column nowrap;
- margin: 67px 0 50px;
- max-width: 35rem;
-}
-
-.create-password {
- margin: 67px 0 50px;
+ margin: 60px 0 30px 0;
+ position: relative;
}
.import-account {
@@ -81,6 +133,41 @@
width: initial !important;
}
+ .alpha-warning,
+ .alpha-warning-welcome-screen {
+ line-height: 1em;
+ padding: 8px 12px;
+ }
+
+ .first-view-main {
+ height: 100%;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-evenly;
+ margin-top: 12px;
+ }
+
+ .mascara-info {
+ margin-top: 0px;
+ width: 100%;
+ align-items: center;
+ }
+
+ .mascara-info .info {
+ text-align: center;
+ font-size: 16px;
+ margin: 0 10px;
+ padding-left: 0px;
+ }
+
+ .mascara-info :first-child {
+ align-self: center;
+ }
+
+ .first-view-phone-invisible {
+ display: none;
+ }
+
.first-time-flow__input {
width: initial !important;
font-size: 14px !important;
@@ -135,7 +222,7 @@
}
.backup-phrase {
- max-width: 100%;
+ width: 100%;
}
.create-password__title,
@@ -180,11 +267,11 @@
color: #1B344D;
font-size: 16px;
line-height: 23px;
- font-family: Montserrat UltraLight;
+ font-family: Roboto;
}
.buy-ether__small-body-text {
- font-family: Montserrat UltraLight;
+ font-family: Roboto;
height: 14px;
color: #757575;
font-size: 12px;
@@ -212,7 +299,7 @@
height: 334px;
overflow-y: auto;
color: #757575;
- font-family: Montserrat UltraLight;
+ font-family: Roboto;
font-size: 12px;
line-height: 15px;
text-align: justify;
@@ -221,7 +308,18 @@
.backup-phrase__content-wrapper {
display: flex;
- flex: row nowrap;
+ flex-flow: row wrap;
+ justify-content: space-between;
+}
+
+.backup-phrase__phrase {
+ flex-grow: .5;
+ min-width: 0;
+}
+
+.backup-phrase__next-button {
+ flex-grow: 1;
+ width: 100%;
}
.backup-phrase__body-text {
@@ -229,15 +327,33 @@
}
.backup-phrase__tips {
- margin: 40px 85px;
+ margin-top: 40px;
width: 285px;
+ flex-grow: .5;
+ min-width: 0;
}
.backup-phrase__tips-text {
color: #5B5D67;
font-size: 16px;
line-height: 23px;
- font-family: Montserrat UltraLight;
+ font-family: Roboto;
+ min-width: 0;
+}
+
+@media only screen and (max-width: 768px) {
+ .backup-phrase__content-wrapper {
+ flex-direction: column;
+ }
+
+ .backup-phrase__phrase {
+ flex: 1 0 auto;
+ }
+
+ .backup-phrase__tips {
+ width: 100%;
+ flex: 1 0 auto;
+ }
}
.backup-phrase__secret {
@@ -255,7 +371,7 @@
.backup-phrase__secret-words {
width: 310px;
color: #5B5D67;
- font-family: Montserrat Light;
+ font-family: Roboto;
font-size: 20px;
line-height: 26px;
text-align: center;
@@ -284,7 +400,7 @@
background: none;
box-shadow: none;
color: #FFFFFF;
- font-family: Montserrat Regular;
+ font-family: Roboto;
font-size: 12px;
font-weight: bold;
line-height: 15px;
@@ -294,13 +410,17 @@
}
.backup-phrase__back-button,
-.backup-phrase__back-button:hover,
-.import-account__back-button,
-.import-account__back-button:hover {
+.import-account__back-button {
margin-bottom: 18px;
- color: #22232C;
+ color: #22232c;
font-size: 16px;
line-height: 21px;
+ position: absolute;
+ top: -25px;
+}
+
+.backup-phrase__back-button {
+ top: -30px;
}
button.backup-phrase__reveal-button:hover {
@@ -338,7 +458,7 @@ button.backup-phrase__reveal-button:hover {
.backup-phrase__confirm-seed-option {
color: #5B5D67;
- font-family: Montserrat Light;
+ font-family: Roboto;
font-size: 16px;
line-height: 21px;
background-color: #E7E7E7;
@@ -360,7 +480,7 @@ button.backup-phrase__confirm-seed-option:hover {
.import-account__faq-link {
font-size: 18px;
line-height: 23px;
- font-family: Montserrat Light;
+ font-family: Roboto;
}
.import-account__selector-label {
@@ -375,7 +495,7 @@ button.backup-phrase__confirm-seed-option:hover {
background-color: #FFFFFF;
margin-top: 14px;
color: #5B5D67;
- font-family: Montserrat Light;
+ font-family: Roboto;
font-size: 18px;
line-height: 23px;
padding: 14px 21px;
@@ -390,7 +510,7 @@ button.backup-phrase__confirm-seed-option:hover {
font-size: 18px;
line-height: 23px;
margin-top: 21px;
- font-family: Montserrat UltraLight;
+ font-family: Roboto;
}
.import-account__input-wrapper {
@@ -436,7 +556,7 @@ button.backup-phrase__confirm-seed-option:hover {
border: 1px solid #1B344D;
border-radius: 4px;
color: #1B344D;
- font-family: Montserrat Light;
+ font-family: Roboto;
font-size: 18px;
display: flex;
flex-flow: column nowrap;
@@ -453,7 +573,7 @@ button.backup-phrase__confirm-seed-option:hover {
.import-account__file-name {
color: #000000;
- font-family: Montserrat Light;
+ font-family: Roboto;
font-size: 18px;
line-height: 23px;
margin-left: 22px;
@@ -474,7 +594,7 @@ button.backup-phrase__confirm-seed-option:hover {
.buy-ether__content-headline {
color: #1B344D;
- font-family: Montserrat Light;
+ font-family: Roboto;
font-size: 18px;
line-height: 23px;
}
@@ -503,7 +623,7 @@ button.backup-phrase__confirm-seed-option:hover {
align-items: center;
padding: 20px 0;
color: #9B9B9B;
- font-family: Montserrat Light;
+ font-family: Roboto;
font-size: 14px;
line-height: 18px;
cursor: pointer;
@@ -538,7 +658,7 @@ button.backup-phrase__confirm-seed-option:hover {
.buy-ether__button-separator-text {
font-size: 20px;
line-height: 26px;
- font-family: Montserrat Light;
+ font-family: Roboto;
margin: 35px 0 14px 30px;
display: flex;
flex-flow: column nowrap;
@@ -550,7 +670,7 @@ button.backup-phrase__confirm-seed-option:hover {
color: #1B344D !important;
font-size: 14px !important;
line-height: 18px !important;
- font-family: Montserrat UltraLight !important;
+ font-family: Roboto;
}
.buy-ether__action-content-wrapper {
@@ -584,6 +704,7 @@ button.backup-phrase__confirm-seed-option:hover {
color: #FFFFFF;
font-size: 20px;
font-weight: 500;
+ font-family: Roboto;
line-height: 26px;
text-align: center;
text-transform: uppercase;
@@ -608,7 +729,7 @@ button.first-time-flow__button:hover {
color: #1B344D;
font-size: 20px;
line-height: 26px;
- font-family: Montserrat Light;
+ font-family: Roboto;
text-align: center;
margin: 35px 0 14px;
background-color: transparent;
@@ -660,7 +781,7 @@ button.first-time-flow__button--tertiary:hover {
font-size: 20px;
line-height: 26px;
text-align: center;
- font-family: Montserrat UltraLight;
+ font-family: Roboto;
}
.icon {
@@ -708,7 +829,7 @@ button.first-time-flow__button--tertiary:hover {
.shapeshift-form__deposit-instruction {
color: #757575;
color: rgba(0, 0, 0, 0.45);
- font-family: Montserrat Light;
+ font-family: Roboto;
font-weight: 300;
line-height: 19px;
padding-bottom: 6px;
@@ -725,7 +846,7 @@ button.first-time-flow__button--tertiary:hover {
width: 100%;
height: 45px;
line-height: 44px;
- font-family: Montserrat Light;
+ font-family: Roboto;
}
.shapeshift-form__address-input-label {
@@ -734,7 +855,7 @@ button.first-time-flow__button--tertiary:hover {
font-weight: 500;
line-height: 18px;
padding-bottom: 6px;
- font-family: Montserrat Light;
+ font-family: Roboto;
}
.shapeshift-form__address-input {
@@ -753,7 +874,7 @@ button.first-time-flow__button--tertiary:hover {
.shapeshift-form__address-input-error-message {
color: #FF001F;
- font-family: Montserrat Light;
+ font-family: Roboto;
font-size: 12px;
height: 24px;
line-height: 18px;
@@ -763,7 +884,7 @@ button.first-time-flow__button--tertiary:hover {
display: flex;
flex-flow: row wrap;
color: #9B9B9B;
- font-family: Montserrat Light;
+ font-family: Roboto;
font-size: 10px;
line-height: 16px;
}
diff --git a/mascara/src/app/first-time/notice-screen.js b/mascara/src/app/first-time/notice-screen.js
index 0f0a7e95d..cbd8f9f5b 100644
--- a/mascara/src/app/first-time/notice-screen.js
+++ b/mascara/src/app/first-time/notice-screen.js
@@ -63,26 +63,32 @@ class NoticeScreen extends Component {
return (
isLoading
? <LoadingScreen />
- : <div
- className="tou"
- onScroll={this.onScroll}
- >
- <Identicon address={address} diameter={70} />
- <div className="tou__title">{title}</div>
- <Markdown
- className="tou__body markdown"
- source={body}
- skipHtml
- />
- <button
- className="first-time-flow__button"
- onClick={atBottom && this.acceptTerms}
- disabled={!atBottom}
- >
- Accept
- </button>
- <Breadcrumbs total={3} currentIndex={2} />
- </div>
+ : (
+ <div className="first-view-main-wrapper">
+ <div className="first-view-main">
+ <div
+ className="tou"
+ onScroll={this.onScroll}
+ >
+ <Identicon address={address} diameter={70} />
+ <div className="tou__title">{title}</div>
+ <Markdown
+ className="tou__body markdown"
+ source={body}
+ skipHtml
+ />
+ <button
+ className="first-time-flow__button"
+ onClick={atBottom && this.acceptTerms}
+ disabled={!atBottom}
+ >
+ Accept
+ </button>
+ <Breadcrumbs total={3} currentIndex={2} />
+ </div>
+ </div>
+ </div>
+ )
)
}
}
diff --git a/mascara/src/app/first-time/unique-image-screen.js b/mascara/src/app/first-time/unique-image-screen.js
index 46448aacf..ede17ee3d 100644
--- a/mascara/src/app/first-time/unique-image-screen.js
+++ b/mascara/src/app/first-time/unique-image-screen.js
@@ -12,22 +12,26 @@ class UniqueImageScreen extends Component {
render () {
return (
- <div className="unique-image">
- <Identicon address={this.props.address} diameter={70} />
- <div className="unique-image__title">Your unique account image</div>
- <div className="unique-image__body-text">
- This image was programmatically generated for you by your new account number.
+ <div className="first-view-main-wrapper">
+ <div className="first-view-main">
+ <div className="unique-image">
+ <Identicon address={this.props.address} diameter={70} />
+ <div className="unique-image__title">Your unique account image</div>
+ <div className="unique-image__body-text">
+ This image was programmatically generated for you by your new account number.
+ </div>
+ <div className="unique-image__body-text">
+ You’ll see this image everytime you need to confirm a transaction.
+ </div>
+ <button
+ className="first-time-flow__button"
+ onClick={this.props.next}
+ >
+ Next
+ </button>
+ <Breadcrumbs total={3} currentIndex={1} />
+ </div>
</div>
- <div className="unique-image__body-text">
- You’ll see this image everytime you need to confirm a transaction.
- </div>
- <button
- className="first-time-flow__button"
- onClick={this.props.next}
- >
- Next
- </button>
- <Breadcrumbs total={3} currentIndex={1} />
</div>
)
}
diff --git a/old-ui/app/components/transaction-list-item.js b/old-ui/app/components/transaction-list-item.js
index e7251df8d..7ab3414e5 100644
--- a/old-ui/app/components/transaction-list-item.js
+++ b/old-ui/app/components/transaction-list-item.js
@@ -29,9 +29,16 @@ function TransactionListItem () {
}
TransactionListItem.prototype.showRetryButton = function () {
- const { transaction = {} } = this.props
- const { status, time } = transaction
- return status === 'submitted' && Date.now() - time > 30000
+ const { transaction = {}, transactions } = this.props
+ const { status, submittedTime, txParams } = transaction
+ const currentNonce = txParams.nonce
+ const currentNonceTxs = transactions.filter(tx => tx.txParams.nonce === currentNonce)
+ const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
+ const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[0]
+ const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce
+ && lastSubmittedTxWithCurrentNonce.id === transaction.id
+
+ return currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000
}
TransactionListItem.prototype.render = function () {
@@ -201,6 +208,11 @@ function formatDate (date) {
function renderErrorOrWarning (transaction) {
const { status, err, warning } = transaction
+ // show dropped
+ if (status === 'dropped') {
+ return h('span.dropped', ' (Dropped)')
+ }
+
// show rejected
if (status === 'rejected') {
return h('span.error', ' (Rejected)')
diff --git a/old-ui/app/components/transaction-list.js b/old-ui/app/components/transaction-list.js
index 345e3ca16..c77852921 100644
--- a/old-ui/app/components/transaction-list.js
+++ b/old-ui/app/components/transaction-list.js
@@ -62,7 +62,7 @@ TransactionList.prototype.render = function () {
}
return h(TransactionListItem, {
transaction, i, network, key,
- conversionRate,
+ conversionRate, transactions,
showTx: (txId) => {
this.props.viewPendingTx(txId)
},
diff --git a/old-ui/app/css/index.css b/old-ui/app/css/index.css
index 67c327f62..7af713336 100644
--- a/old-ui/app/css/index.css
+++ b/old-ui/app/css/index.css
@@ -247,6 +247,10 @@ app sections
color: #FFAE00;
}
+.dropped {
+ color: #6195ED;
+}
+
.lock {
width: 50px;
height: 50px;
diff --git a/old-ui/app/keychains/hd/restore-vault.js b/old-ui/app/keychains/hd/restore-vault.js
index 222172dfd..d334d8e5f 100644
--- a/old-ui/app/keychains/hd/restore-vault.js
+++ b/old-ui/app/keychains/hd/restore-vault.js
@@ -140,6 +140,19 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
// check seed
var seedBox = document.querySelector('textarea.twelve-word-phrase')
var seed = seedBox.value.trim()
+
+ // true if the string has more than a space between words.
+ if (seed.split(' ').length > 1) {
+ this.warning = 'there can only be a space between words'
+ this.props.dispatch(actions.displayWarning(this.warning))
+ return
+ }
+ // true if seed contains a character that is not between a-z or a space
+ if (!seed.match(/^[a-z ]+$/)) {
+ this.warning = 'seed words only have lowercase characters'
+ this.props.dispatch(actions.displayWarning(this.warning))
+ return
+ }
if (seed.split(' ').length !== 12) {
this.warning = 'seed phrases are 12 words long'
this.props.dispatch(actions.displayWarning(this.warning))
diff --git a/package-lock.json b/package-lock.json
index 55fa5c838..d1c488b09 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3611,6 +3611,16 @@
"object-assign": "4.1.1"
}
},
+ "cross-env": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.1.4.tgz",
+ "integrity": "sha512-Mx8mw6JWhfpYoEk7PGvHxJMLQwQHORAs8+2bX+C1lGQ4h3GkDb1zbzC2Nw85YH9ZQMlO0BHZxMacgrfPmMFxbg==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "5.1.0",
+ "is-windows": "1.0.2"
+ }
+ },
"cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
@@ -3919,6 +3929,16 @@
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.1.0.tgz",
"integrity": "sha512-ZQVKfRVlwRfD150ndzEK8M90ABT+Y/JQKs4Y7U4MXdpuoUkkrr4DwKbVux3YjylA5bUMUj0Nc3pMxPJX6N2QQQ=="
},
+ "debounce-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/debounce-stream/-/debounce-stream-2.0.0.tgz",
+ "integrity": "sha1-HjNADM/wFavY7DMGYaVitoQQsI8=",
+ "requires": {
+ "debounce": "1.1.0",
+ "duplexer": "0.1.1",
+ "through": "2.3.8"
+ }
+ },
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
diff --git a/package.json b/package.json
index c5d9d3c45..8f05bc7f1 100644
--- a/package.json
+++ b/package.json
@@ -6,15 +6,16 @@
"scripts": {
"start": "npm run dev",
"dev": "gulp dev --debug",
- "ui": "npm run test:flat:build:states && beefy ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
- "mock": "beefy mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
+ "ui": "npm run test:flat:build:states && beefy development/ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
+ "mock": "beefy development/mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
"watch": "mocha watch --recursive \"test/unit/**/*.js\"",
- "mascara": "gulp build && METAMASK_DEBUG=true node ./mascara/example/server",
- "dist": "npm run dist:clear && npm install && gulp dist",
+ "mascara": "gulp build && cross-env METAMASK_DEBUG=true node ./mascara/example/server",
+ "dist": "npm run dist:clear && npm install && gulp dist && npm run test:es5",
"dist:clear": "rm -rf node_modules/eth-contract-metadata && rm -rf node_modules/eth-phishing-detect",
"test": "npm run lint && npm run test:coverage && npm run test:integration",
- "test:unit": "METAMASK_ENV=test mocha --exit --require babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"",
- "test:single": "METAMASK_ENV=test mocha --require test/helper.js",
+ "test:es5": "es-check es5 ./dist/**/*.js",
+ "test:unit": "cross-env METAMASK_ENV=test mocha --exit --require babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"",
+ "test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js",
"test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara",
"test:integration:build": "gulp build:scss",
"test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload",
@@ -23,9 +24,9 @@
"test:flat:build": "npm run test:flat:build:ui && npm run test:flat:build:tests",
"test:flat:build:tests": "node test/integration/index.js",
"test:flat:build:states": "node development/genStates.js",
- "test:flat:build:ui": "npm run test:flat:build:states && browserify ./mock-dev.js -o ./development/bundle.js",
+ "test:flat:build:ui": "npm run test:flat:build:states && browserify ./development/mock-dev.js -o ./development/bundle.js",
"test:mascara": "npm run test:mascara:build && karma start test/mascara.conf.js",
- "test:mascara:build": "mkdir -p dist/mascara && npm run test:mascara:build:ui && npm run test:mascara:build:background && npm run test:mascara:build:tests",
+ "test:mascara:build": "mkdirp dist/mascara && npm run test:mascara:build:ui && npm run test:mascara:build:background && npm run test:mascara:build:tests",
"test:mascara:build:ui": "browserify mascara/test/test-ui.js -o dist/mascara/ui.js",
"test:mascara:build:background": "browserify mascara/src/background.js -o dist/mascara/background.js",
"test:mascara:build:tests": "browserify test/integration/lib/first-time.js -o dist/mascara/tests.js",
@@ -72,6 +73,7 @@
"clone": "^2.1.1",
"copy-to-clipboard": "^3.0.8",
"debounce": "^1.0.0",
+ "debounce-stream": "^2.0.0",
"deep-extend": "^0.5.0",
"detect-node": "^2.0.3",
"disc": "^1.3.2",
@@ -193,11 +195,13 @@
"chai": "^4.1.0",
"compression": "^1.7.1",
"coveralls": "^3.0.0",
+ "cross-env": "^5.1.4",
"deep-freeze-strict": "^1.1.1",
"del": "^3.0.0",
"envify": "^4.0.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-15": "^1.0.5",
+ "es-check": "^2.0.2",
"eslint-plugin-chai": "0.0.1",
"eslint-plugin-mocha": "^4.9.0",
"eslint-plugin-react": "^7.4.0",
diff --git a/test/base.conf.js b/test/base.conf.js
index 82b9d8eec..adb5357e8 100644
--- a/test/base.conf.js
+++ b/test/base.conf.js
@@ -46,7 +46,9 @@ module.exports = function(config) {
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
- browsers: ['Chrome', 'Firefox'],
+ browsers: process.env.browsers ?
+ JSON.parse(process.env.browsers)
+ : ['Chrome', 'Firefox'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
diff --git a/test/integration/index.js b/test/integration/index.js
index 144303dbb..b266ddf37 100644
--- a/test/integration/index.js
+++ b/test/integration/index.js
@@ -23,4 +23,4 @@ pump(
console.log(`Integration test build completed: "${bundlePath}"`)
process.exit(0)
}
-) \ No newline at end of file
+)
diff --git a/test/integration/lib/add-token.js b/test/integration/lib/add-token.js
index dd4251cc4..42ed28dca 100644
--- a/test/integration/lib/add-token.js
+++ b/test/integration/lib/add-token.js
@@ -1,4 +1,9 @@
const reactTriggerChange = require('react-trigger-change')
+const {
+ timeout,
+ queryAsync,
+ findAsync,
+} = require('../../lib/util')
QUnit.module('Add token flow')
@@ -13,74 +18,60 @@ QUnit.test('successful add token flow', (assert) => {
})
async function runAddTokenFlowTest (assert, done) {
- const selectState = $('select')
+ const selectState = await queryAsync($, 'select')
selectState.val('add token')
reactTriggerChange(selectState[0])
- await timeout(2000)
-
// Check that no tokens have been added
assert.ok($('.token-list-item').length === 0, 'no tokens added')
// Go to Add Token screen
- let addTokenButton = $('button.btn-clear.wallet-view__add-token-button')
+ let addTokenButton = await queryAsync($, 'button.btn-clear.wallet-view__add-token-button')
assert.ok(addTokenButton[0], 'add token button present')
addTokenButton[0].click()
- await timeout(1000)
-
// Verify Add Token screen
- let addTokenWrapper = $('.add-token__wrapper')
+ let addTokenWrapper = await queryAsync($, '.add-token__wrapper')
assert.ok(addTokenWrapper[0], 'add token wrapper renders')
- let addTokenTitle = $('.add-token__title')
+ let addTokenTitle = await queryAsync($, '.add-token__title')
assert.equal(addTokenTitle[0].textContent, 'Add Token', 'add token title is correct')
// Cancel Add Token
- const cancelAddTokenButton = $('button.btn-cancel.add-token__button')
+ const cancelAddTokenButton = await queryAsync($, 'button.btn-cancel.add-token__button')
assert.ok(cancelAddTokenButton[0], 'cancel add token button present')
cancelAddTokenButton.click()
- await timeout(1000)
-
assert.ok($('.wallet-view')[0], 'cancelled and returned to account detail wallet view')
// Return to Add Token Screen
- addTokenButton = $('button.btn-clear.wallet-view__add-token-button')
+ addTokenButton = await queryAsync($, 'button.btn-clear.wallet-view__add-token-button')
assert.ok(addTokenButton[0], 'add token button present')
addTokenButton[0].click()
- await timeout(1000)
-
// Verify Add Token Screen
- addTokenWrapper = $('.add-token__wrapper')
- addTokenTitle = $('.add-token__title')
+ addTokenWrapper = await queryAsync($, '.add-token__wrapper')
+ addTokenTitle = await queryAsync($, '.add-token__title')
assert.ok(addTokenWrapper[0], 'add token wrapper renders')
assert.equal(addTokenTitle[0].textContent, 'Add Token', 'add token title is correct')
// Search for token
- const searchInput = $('input.add-token__input')
+ const searchInput = await queryAsync($, 'input.add-token__input')
searchInput.val('a')
reactTriggerChange(searchInput[0])
- await timeout()
-
// Click token to add
- const tokenWrapper = $('div.add-token__token-wrapper')
+ const tokenWrapper = await queryAsync($, 'div.add-token__token-wrapper')
assert.ok(tokenWrapper[0], 'token found')
const tokenImageProp = tokenWrapper.find('.add-token__token-icon').css('background-image')
const tokenImageUrl = tokenImageProp.slice(5, -2)
tokenWrapper[0].click()
- await timeout()
-
// Click Next button
- let nextButton = $('button.btn-clear.add-token__button')
+ let nextButton = await queryAsync($, 'button.btn-clear.add-token__button')
assert.equal(nextButton[0].textContent, 'Next', 'next button rendered')
nextButton[0].click()
- await timeout()
-
// Confirm Add token
assert.equal(
$('.add-token__description')[0].textContent,
@@ -90,47 +81,35 @@ async function runAddTokenFlowTest (assert, done) {
assert.ok($('button.btn-clear.add-token__button')[0], 'confirm add token button found')
$('button.btn-clear.add-token__button')[0].click()
- await timeout(2000)
-
// Verify added token image
- let heroBalance = $('.hero-balance')
+ let heroBalance = await queryAsync($, '.hero-balance')
assert.ok(heroBalance, 'rendered hero balance')
assert.ok(tokenImageUrl.indexOf(heroBalance.find('img').attr('src')) > -1, 'token added')
// Return to Add Token Screen
- addTokenButton = $('button.btn-clear.wallet-view__add-token-button')
+ addTokenButton = await queryAsync($, 'button.btn-clear.wallet-view__add-token-button')
assert.ok(addTokenButton[0], 'add token button present')
addTokenButton[0].click()
- await timeout(1000)
-
- const addCustom = $('.add-token__add-custom')
+ const addCustom = await queryAsync($, '.add-token__add-custom')
assert.ok(addCustom[0], 'add custom token button present')
addCustom[0].click()
- await timeout()
-
// Input token contract address
- const customInput = $('input.add-token__add-custom-input')
+ const customInput = await queryAsync($, 'input.add-token__add-custom-input')
customInput.val('0x177af043D3A1Aed7cc5f2397C70248Fc6cDC056c')
reactTriggerChange(customInput[0])
- await timeout(1000)
-
// Click Next button
- nextButton = $('button.btn-clear.add-token__button')
+ nextButton = await queryAsync($, 'button.btn-clear.add-token__button')
assert.equal(nextButton[0].textContent, 'Next', 'next button rendered')
nextButton[0].click()
- await timeout(1000)
-
// Verify symbol length error since contract address won't return symbol
- const errorMessage = $('.add-token__add-custom-error-message')
+ const errorMessage = await queryAsync($, '.add-token__add-custom-error-message')
assert.ok(errorMessage[0], 'error rendered')
$('button.btn-cancel.add-token__button')[0].click()
- await timeout(2000)
-
// // Confirm Add token
// assert.equal(
// $('.add-token__description')[0].textContent,
@@ -141,13 +120,7 @@ async function runAddTokenFlowTest (assert, done) {
// $('button.btn-clear.add-token__button')[0].click()
// // Verify added token image
- // heroBalance = $('.hero-balance')
+ // heroBalance = await queryAsync($, '.hero-balance')
// assert.ok(heroBalance, 'rendered hero balance')
// assert.ok(heroBalance.find('.identicon')[0], 'token added')
}
-
-function timeout (time) {
- return new Promise((resolve, reject) => {
- setTimeout(resolve, time || 1500)
- })
-}
diff --git a/test/integration/lib/confirm-sig-requests.js b/test/integration/lib/confirm-sig-requests.js
index e49424c37..9737a2283 100644
--- a/test/integration/lib/confirm-sig-requests.js
+++ b/test/integration/lib/confirm-sig-requests.js
@@ -1,5 +1,9 @@
const reactTriggerChange = require('react-trigger-change')
-
+const {
+ timeout,
+ queryAsync,
+ findAsync,
+} = require('../../lib/util')
const PASSWORD = 'password123'
QUnit.module('confirm sig requests')
@@ -13,55 +17,41 @@ QUnit.test('successful confirmation of sig requests', (assert) => {
})
async function runConfirmSigRequestsTest(assert, done) {
- let selectState = $('select')
+ let selectState = await queryAsync($, 'select')
selectState.val('confirm sig requests')
reactTriggerChange(selectState[0])
- await timeout(2000)
-
- let confirmSigHeadline = $('.request-signature__headline')
+ let confirmSigHeadline = await queryAsync($, '.request-signature__headline')
assert.equal(confirmSigHeadline[0].textContent, 'Your signature is being requested')
- let confirmSigRowValue = $('.request-signature__row-value')
+ let confirmSigRowValue = await queryAsync($, '.request-signature__row-value')
assert.ok(confirmSigRowValue[0].textContent.match(/^\#\sTerms\sof\sUse/))
- let confirmSigSignButton = $('.request-signature__footer__sign-button')
+ let confirmSigSignButton = await queryAsync($, '.request-signature__footer__sign-button')
confirmSigSignButton[0].click()
- await timeout(2000)
-
- confirmSigHeadline = $('.request-signature__headline')
+ confirmSigHeadline = await queryAsync($, '.request-signature__headline')
assert.equal(confirmSigHeadline[0].textContent, 'Your signature is being requested')
- let confirmSigMessage = $('.request-signature__notice')
+ let confirmSigMessage = await queryAsync($, '.request-signature__notice')
assert.ok(confirmSigMessage[0].textContent.match(/^Signing\sthis\smessage/))
- confirmSigRowValue = $('.request-signature__row-value')
+ confirmSigRowValue = await queryAsync($, '.request-signature__row-value')
assert.equal(confirmSigRowValue[0].textContent, '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0')
- confirmSigSignButton = $('.request-signature__footer__sign-button')
+ confirmSigSignButton = await queryAsync($, '.request-signature__footer__sign-button')
confirmSigSignButton[0].click()
- await timeout(2000)
-
- confirmSigHeadline = $('.request-signature__headline')
+ confirmSigHeadline = await queryAsync($, '.request-signature__headline')
assert.equal(confirmSigHeadline[0].textContent, 'Your signature is being requested')
- confirmSigRowValue = $('.request-signature__row-value')
+ confirmSigRowValue = await queryAsync($, '.request-signature__row-value')
assert.equal(confirmSigRowValue[0].textContent, 'Hi, Alice!')
assert.equal(confirmSigRowValue[1].textContent, '1337')
- confirmSigSignButton = $('.request-signature__footer__sign-button')
+ confirmSigSignButton = await queryAsync($, '.request-signature__footer__sign-button')
confirmSigSignButton[0].click()
- await timeout(2000)
-
- const txView = $('.tx-view')
+ const txView = await queryAsync($, '.tx-view')
assert.ok(txView[0], 'Should return to the account details screen after confirming')
}
-
-function timeout (time) {
- return new Promise((resolve, reject) => {
- setTimeout(resolve, time || 1500)
- })
-} \ No newline at end of file
diff --git a/test/integration/lib/first-time.js b/test/integration/lib/first-time.js
index 764eae47c..052d89518 100644
--- a/test/integration/lib/first-time.js
+++ b/test/integration/lib/first-time.js
@@ -1,6 +1,10 @@
const reactTriggerChange = require('react-trigger-change')
const PASSWORD = 'password123'
const runMascaraFirstTimeTest = require('./mascara-first-time')
+const {
+ timeout,
+ findAsync,
+} = require('../../lib/util')
QUnit.module('first time usage')
@@ -21,20 +25,19 @@ async function runFirstTimeUsageTest(assert, done) {
selectState.val('first time')
reactTriggerChange(selectState[0])
- await timeout(2000)
const app = $('#app-content')
// recurse notices
while (true) {
- const button = app.find('button')
+ const button = await findAsync(app, 'button')
if (button.html() === 'Accept') {
// still notices to accept
- const termsPage = app.find('.markdown')[0]
+ const termsPageRaw = await findAsync(app, '.markdown')
+ const termsPage = (await findAsync(app, '.markdown'))[0]
+ console.log('termsPageRaw', termsPageRaw)
termsPage.scrollTop = termsPage.scrollHeight
- await timeout()
console.log('Clearing notice')
button.click()
- await timeout()
} else {
// exit loop
console.log('No more notices...')
@@ -42,97 +45,68 @@ async function runFirstTimeUsageTest(assert, done) {
}
}
- await timeout()
-
// Scroll through terms
- const title = app.find('h1')[0]
+ const title = (await findAsync(app, 'h1'))[0]
assert.equal(title.textContent, 'MetaMask', 'title screen')
// enter password
- const pwBox = app.find('#password-box')[0]
- const confBox = app.find('#password-box-confirm')[0]
+ const pwBox = (await findAsync(app, '#password-box'))[0]
+ const confBox = (await findAsync(app, '#password-box-confirm'))[0]
pwBox.value = PASSWORD
confBox.value = PASSWORD
- await timeout()
-
// create vault
- const createButton = app.find('button.primary')[0]
+ const createButton = (await findAsync(app, 'button.primary'))[0]
createButton.click()
- await timeout(3000)
-
- const created = app.find('h3')[0]
+ await timeout()
+ const created = (await findAsync(app, 'h3'))[0]
assert.equal(created.textContent, 'Vault Created', 'Vault created screen')
// Agree button
- const button = app.find('button')[0]
+ const button = (await findAsync(app, 'button'))[0]
assert.ok(button, 'button present')
button.click()
- await timeout(1000)
-
- const detail = app.find('.account-detail-section')[0]
+ const detail = (await findAsync(app, '.account-detail-section'))[0]
assert.ok(detail, 'Account detail section loaded.')
- const sandwich = app.find('.sandwich-expando')[0]
+ const sandwich = (await findAsync(app, '.sandwich-expando'))[0]
sandwich.click()
- await timeout()
-
- const menu = app.find('.menu-droppo')[0]
+ const menu = (await findAsync(app, '.menu-droppo'))[0]
const children = menu.children
const logout = children[2]
assert.ok(logout, 'Lock menu item found')
logout.click()
- await timeout(1000)
-
- const pwBox2 = app.find('#password-box')[0]
+ const pwBox2 = (await findAsync(app, '#password-box'))[0]
pwBox2.value = PASSWORD
- const createButton2 = app.find('button.primary')[0]
+ const createButton2 = (await findAsync(app, 'button.primary'))[0]
createButton2.click()
- await timeout(1000)
-
- const detail2 = app.find('.account-detail-section')[0]
+ const detail2 = (await findAsync(app, '.account-detail-section'))[0]
assert.ok(detail2, 'Account detail section loaded again.')
- await timeout()
-
// open account settings dropdown
- const qrButton = app.find('.fa.fa-ellipsis-h')[0]
+ const qrButton = (await findAsync(app, '.fa.fa-ellipsis-h'))[0]
qrButton.click()
- await timeout(1000)
-
// qr code item
- const qrButton2 = app.find('.dropdown-menu-item')[1]
+ const qrButton2 = (await findAsync(app, '.dropdown-menu-item'))[1]
qrButton2.click()
- await timeout(1000)
-
- const qrHeader = app.find('.qr-header')[0]
- const qrContainer = app.find('#qr-container')[0]
+ const qrHeader = (await findAsync(app, '.qr-header'))[0]
+ const qrContainer = (await findAsync(app, '#qr-container'))[0]
assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.')
assert.ok(qrContainer, 'QR Container found')
- await timeout()
-
- const networkMenu = app.find('.network-indicator')[0]
+ const networkMenu = (await findAsync(app, '.network-indicator'))[0]
networkMenu.click()
- await timeout()
-
- const networkMenu2 = app.find('.network-indicator')[0]
+ const networkMenu2 = (await findAsync(app, '.network-indicator'))[0]
const children2 = networkMenu2.children
children2.length[3]
assert.ok(children2, 'All network options present')
}
-
-function timeout (time) {
- return new Promise((resolve, reject) => {
- setTimeout(resolve, time || 1500)
- })
-} \ No newline at end of file
diff --git a/test/integration/lib/mascara-first-time.js b/test/integration/lib/mascara-first-time.js
index 515c7f383..bcbc94ff6 100644
--- a/test/integration/lib/mascara-first-time.js
+++ b/test/integration/lib/mascara-first-time.js
@@ -1,119 +1,95 @@
const PASSWORD = 'password123'
const reactTriggerChange = require('react-trigger-change')
+const {
+ timeout,
+ findAsync,
+ queryAsync,
+} = require('../../lib/util')
async function runFirstTimeUsageTest (assert, done) {
await timeout(4000)
- const app = $('#app-content')
+ const app = await queryAsync($, '#app-content')
await skipNotices(app)
- await timeout()
-
// Scroll through terms
- const title = app.find('.create-password__title').text()
+ const title = (await findAsync(app, '.create-password__title')).text()
assert.equal(title, 'Create Password', 'create password screen')
// enter password
- const pwBox = app.find('.first-time-flow__input')[0]
- const confBox = app.find('.first-time-flow__input')[1]
+ const pwBox = (await findAsync(app, '.first-time-flow__input'))[0]
+ const confBox = (await findAsync(app, '.first-time-flow__input'))[1]
pwBox.value = PASSWORD
confBox.value = PASSWORD
reactTriggerChange(pwBox)
reactTriggerChange(confBox)
-
- await timeout()
-
// Create Password
- const createButton = app.find('button.first-time-flow__button')[0]
+ const createButton = (await findAsync(app, 'button.first-time-flow__button'))[0]
createButton.click()
- await timeout(3000)
-
- const created = app.find('.unique-image__title')[0]
+ const created = (await findAsync(app, '.unique-image__title'))[0]
assert.equal(created.textContent, 'Your unique account image', 'unique image screen')
// Agree button
- let button = app.find('button')[0]
+ let button = (await findAsync(app, 'button'))[0]
assert.ok(button, 'button present')
button.click()
- await timeout(1000)
-
await skipNotices(app)
// secret backup phrase
- const seedTitle = app.find('.backup-phrase__title')[0]
+ const seedTitle = (await findAsync(app, '.backup-phrase__title'))[0]
assert.equal(seedTitle.textContent, 'Secret Backup Phrase', 'seed phrase screen')
- app.find('.backup-phrase__reveal-button').click()
-
- await timeout(1000)
- const seedPhrase = app.find('.backup-phrase__secret-words').text().split(' ')
- app.find('.first-time-flow__button').click()
+ ;(await findAsync(app, '.backup-phrase__reveal-button')).click()
+ const seedPhrase = (await findAsync(app, '.backup-phrase__secret-words')).text().split(' ')
+ ;(await findAsync(app, '.first-time-flow__button')).click()
+ await timeout()
const selectPhrase = text => {
const option = $('.backup-phrase__confirm-seed-option')
.filter((i, d) => d.textContent === text)[0]
-
$(option).click()
}
- await timeout(1000)
-
seedPhrase.forEach(sp => selectPhrase(sp))
- app.find('.first-time-flow__button').click()
- await timeout(1000)
+ ;(await findAsync(app, '.first-time-flow__button')).click()
// Deposit Ether Screen
- const buyEthTitle = app.find('.buy-ether__title')[0]
+ const buyEthTitle = (await findAsync(app, '.buy-ether__title'))[0]
assert.equal(buyEthTitle.textContent, 'Deposit Ether', 'deposit ether screen')
- app.find('.buy-ether__do-it-later').click()
- await timeout(1000)
+ ;(await findAsync(app, '.buy-ether__do-it-later')).click()
- const menu = app.find('.account-menu__icon')[0]
+ const menu = (await findAsync(app, '.account-menu__icon'))[0]
menu.click()
- await timeout()
-
- const lock = app.find('.account-menu__logout-button')[0]
+ const lock = (await findAsync(app, '.account-menu__logout-button'))[0]
assert.ok(lock, 'Lock menu item found')
lock.click()
- await timeout(1000)
-
- const pwBox2 = app.find('#password-box')[0]
+ const pwBox2 = (await findAsync(app, '#password-box'))[0]
pwBox2.value = PASSWORD
- const createButton2 = app.find('button.primary')[0]
+ const createButton2 = (await findAsync(app, 'button.primary'))[0]
createButton2.click()
- await timeout(1000)
-
- const detail2 = app.find('.wallet-view')[0]
+ const detail2 = (await findAsync(app, '.wallet-view'))[0]
assert.ok(detail2, 'Account detail section loaded again.')
- await timeout()
-
// open account settings dropdown
- const qrButton = app.find('.wallet-view__details-button')[0]
+ const qrButton = (await findAsync(app, '.wallet-view__details-button'))[0]
qrButton.click()
- await timeout(1000)
-
- const qrHeader = app.find('.editable-label__value')[0]
- const qrContainer = app.find('.qr-wrapper')[0]
+ const qrHeader = (await findAsync(app, '.editable-label__value'))[0]
+ const qrContainer = (await findAsync(app, '.qr-wrapper'))[0]
assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.')
assert.ok(qrContainer, 'QR Container found')
- await timeout()
-
- const networkMenu = app.find('.network-component')[0]
+ const networkMenu = (await findAsync(app, '.network-component'))[0]
networkMenu.click()
- await timeout()
-
- const networkMenu2 = app.find('.network-indicator')[0]
+ const networkMenu2 = (await findAsync(app, '.network-indicator'))[0]
const children2 = networkMenu2.children
children2.length[3]
assert.ok(children2, 'All network options present')
@@ -121,18 +97,12 @@ async function runFirstTimeUsageTest (assert, done) {
module.exports = runFirstTimeUsageTest
-function timeout (time) {
- return new Promise((resolve, reject) => {
- setTimeout(resolve, time || 1500)
- })
-}
-
async function skipNotices (app) {
while (true) {
- const button = app.find('button')
+ const button = await findAsync(app, 'button')
if (button && button.html() === 'Accept') {
// still notices to accept
- const termsPage = app.find('.markdown')[0]
+ const termsPage = (await findAsync(app, '.markdown'))[0]
if (!termsPage) {
break
}
diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js
index 3456f2367..573faaee3 100644
--- a/test/integration/lib/send-new-ui.js
+++ b/test/integration/lib/send-new-ui.js
@@ -1,4 +1,9 @@
-const reactTriggerChange = require('react-trigger-change')
+const reactTriggerChange = require('../../lib/react-trigger-change')
+const {
+ timeout,
+ queryAsync,
+ findAsync,
+} = require('../../lib/util')
const PASSWORD = 'password123'
@@ -18,83 +23,65 @@ global.ethQuery = {
async function runSendFlowTest(assert, done) {
console.log('*** start runSendFlowTest')
- const selectState = $('select')
+ const selectState = await queryAsync($, 'select')
selectState.val('send new ui')
reactTriggerChange(selectState[0])
- await timeout(2000)
-
- const sendScreenButton = $('button.btn-clear.hero-balance-button')
+ const sendScreenButton = await queryAsync($, 'button.btn-clear.hero-balance-button')
assert.ok(sendScreenButton[1], 'send screen button present')
sendScreenButton[1].click()
- await timeout(1000)
-
- const sendTitle = $('.page-container__title')
+ const sendTitle = await queryAsync($, '.page-container__title')
assert.equal(sendTitle[0].textContent, 'Send ETH', 'Send screen title is correct')
- const sendCopy = $('.page-container__subtitle')
+ const sendCopy = await queryAsync($, '.page-container__subtitle')
assert.equal(sendCopy[0].textContent, 'Only send ETH to an Ethereum address.', 'Send screen has copy')
- const sendFromField = $('.send-v2__form-field')
+ const sendFromField = await queryAsync($, '.send-v2__form-field')
assert.ok(sendFromField[0], 'send screen has a from field')
- let sendFromFieldItemAddress = $('.account-list-item__account-name')
+ let sendFromFieldItemAddress = await queryAsync($, '.account-list-item__account-name')
assert.equal(sendFromFieldItemAddress[0].textContent, 'Send Account 4', 'send from field shows correct account name')
- const sendFromFieldItem = $('.account-list-item')
+ const sendFromFieldItem = await queryAsync($, '.account-list-item')
sendFromFieldItem[0].click()
- await timeout()
-
- const sendFromDropdownList = $('.send-v2__from-dropdown__list')
+ // this seems to fail if the firefox window is not in focus...
+ const sendFromDropdownList = await queryAsync($, '.send-v2__from-dropdown__list')
assert.equal(sendFromDropdownList.children().length, 4, 'send from dropdown shows all accounts')
- console.log(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sendFromDropdownList.children()[1]`, sendFromDropdownList.children()[1]);
sendFromDropdownList.children()[1].click()
- await timeout()
-
- sendFromFieldItemAddress = $('.account-list-item__account-name')
- console.log(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sendFromFieldItemAddress[0]`, sendFromFieldItemAddress[0]);
+ sendFromFieldItemAddress = await queryAsync($, '.account-list-item__account-name')
assert.equal(sendFromFieldItemAddress[0].textContent, 'Send Account 2', 'send from field dropdown changes account name')
- let sendToFieldInput = $('.send-v2__to-autocomplete__input')
+ let sendToFieldInput = await queryAsync($, '.send-v2__to-autocomplete__input')
sendToFieldInput[0].focus()
- await timeout()
-
- const sendToDropdownList = $('.send-v2__from-dropdown__list')
+ const sendToDropdownList = await queryAsync($, '.send-v2__from-dropdown__list')
assert.equal(sendToDropdownList.children().length, 5, 'send to dropdown shows all accounts and address book accounts')
sendToDropdownList.children()[2].click()
- await timeout()
-
const sendToAccountAddress = sendToFieldInput.val()
assert.equal(sendToAccountAddress, '0x2f8d4a878cfa04a6e60d46362f5644deab66572d', 'send to dropdown selects the correct address')
- const sendAmountField = $('.send-v2__form-row:eq(2)')
+ const sendAmountField = await queryAsync($, '.send-v2__form-row:eq(2)')
sendAmountField.find('.currency-display')[0].click()
- await timeout()
-
- const sendAmountFieldInput = sendAmountField.find('input:text')
+ const sendAmountFieldInput = await findAsync(sendAmountField, 'input:text')
sendAmountFieldInput.val('5.1')
reactTriggerChange(sendAmountField.find('input')[0])
- await timeout()
-
- let errorMessage = $('.send-v2__error')
+ let errorMessage = await queryAsync($, '.send-v2__error')
assert.equal(errorMessage[0].textContent, 'Insufficient funds.', 'send should render an insufficient fund error message')
sendAmountFieldInput.val('2.0')
reactTriggerChange(sendAmountFieldInput[0])
-
await timeout()
errorMessage = $('.send-v2__error')
assert.equal(errorMessage.length, 0, 'send should stop rendering amount error message after amount is corrected')
- const sendGasField = $('.send-v2__gas-fee-display')
+ const sendGasField = await queryAsync($, '.send-v2__gas-fee-display')
assert.equal(
sendGasField.find('.currency-display__input-wrapper > input').val(),
'0.000198',
@@ -106,120 +93,86 @@ async function runSendFlowTest(assert, done) {
'send gas field should show estimated gas total converted to USD'
)
- const sendGasOpenCustomizeModalButton = $('.send-v2__sliders-icon-container'
- )
+ const sendGasOpenCustomizeModalButton = await queryAsync($, '.sliders-icon-container')
sendGasOpenCustomizeModalButton[0].click()
- await timeout(1000)
-
- const customizeGasModal = $('.send-v2__customize-gas')
+ const customizeGasModal = await queryAsync($, '.send-v2__customize-gas')
assert.ok(customizeGasModal[0], 'should render the customize gas modal')
- const customizeGasPriceInput = $('.send-v2__gas-modal-card').first().find('input')
+ const customizeGasPriceInput = (await queryAsync($, '.send-v2__gas-modal-card')).first().find('input')
customizeGasPriceInput.val(50)
reactTriggerChange(customizeGasPriceInput[0])
- const customizeGasLimitInput = $('.send-v2__gas-modal-card').last().find('input')
+ const customizeGasLimitInput = (await queryAsync($, '.send-v2__gas-modal-card')).last().find('input')
customizeGasLimitInput.val(60000)
reactTriggerChange(customizeGasLimitInput[0])
- await timeout()
-
- const customizeGasSaveButton = $('.send-v2__customize-gas__save')
+ const customizeGasSaveButton = await queryAsync($, '.send-v2__customize-gas__save')
customizeGasSaveButton[0].click()
- await timeout()
-
assert.equal(
- sendGasField.find('.currency-display__input-wrapper > input').val(),
+ (await findAsync(sendGasField, '.currency-display__input-wrapper > input')).val(),
'0.003',
'send gas field should show customized gas total'
)
assert.equal(
- sendGasField.find('.currency-display__converted-value')[0].textContent,
+ (await findAsync(sendGasField, '.currency-display__converted-value'))[0].textContent,
'3.60 USD',
'send gas field should show customized gas total converted to USD'
)
- const sendButton = $('button.btn-clear.page-container__footer-button')
+ const sendButton = await queryAsync($, 'button.btn-clear.page-container__footer-button')
assert.equal(sendButton[0].textContent, 'Next', 'next button rendered')
sendButton[0].click()
-
- await timeout(2000)
+ await timeout()
selectState.val('send edit')
reactTriggerChange(selectState[0])
- await timeout(2000)
-
- const confirmFromName = $('.confirm-screen-account-name').first()
+ const confirmFromName = (await queryAsync($, '.confirm-screen-account-name')).first()
assert.equal(confirmFromName[0].textContent, 'Send Account 2', 'confirm screen should show correct from name')
- const confirmToName = $('.confirm-screen-account-name').last()
+ const confirmToName = (await queryAsync($, '.confirm-screen-account-name')).last()
assert.equal(confirmToName[0].textContent, 'Send Account 3', 'confirm screen should show correct to name')
- const confirmScreenRows = $('.confirm-screen-rows')
- const confirmScreenGas = confirmScreenRows.find('.confirm-screen-row-info')[2]
- assert.equal(confirmScreenGas.textContent, '3.6 USD', 'confirm screen should show correct gas')
- const confirmScreenTotal = confirmScreenRows.find('.confirm-screen-row-info')[3]
+ const confirmScreenRows = await queryAsync($, '.confirm-screen-rows')
+ const confirmScreenGas = confirmScreenRows.find('.currency-display__converted-value')[0]
+ assert.equal(confirmScreenGas.textContent, '3.60 USD', 'confirm screen should show correct gas')
+ const confirmScreenTotal = confirmScreenRows.find('.confirm-screen-row-info')[2]
assert.equal(confirmScreenTotal.textContent, '2405.36 USD', 'confirm screen should show correct total')
- const confirmScreenBackButton = $('.confirm-screen-back-button')
+ const confirmScreenBackButton = await queryAsync($, '.confirm-screen-back-button')
confirmScreenBackButton[0].click()
- await timeout(1000)
-
- const sendFromFieldItemInEdit = $('.account-list-item')
+ const sendFromFieldItemInEdit = await queryAsync($, '.account-list-item')
sendFromFieldItemInEdit[0].click()
- await timeout()
-
- const sendFromDropdownListInEdit = $('.send-v2__from-dropdown__list')
+ const sendFromDropdownListInEdit = await queryAsync($, '.send-v2__from-dropdown__list')
sendFromDropdownListInEdit.children()[2].click()
- await timeout()
-
- const sendToFieldInputInEdit = $('.send-v2__to-autocomplete__input')
+ const sendToFieldInputInEdit = await queryAsync($, '.send-v2__to-autocomplete__input')
sendToFieldInputInEdit[0].focus()
sendToFieldInputInEdit.val('0xd85a4b6a394794842887b8284293d69163007bbb')
- await timeout()
-
- const sendAmountFieldInEdit = $('.send-v2__form-row:eq(2)')
+ const sendAmountFieldInEdit = await queryAsync($, '.send-v2__form-row:eq(2)')
sendAmountFieldInEdit.find('.currency-display')[0].click()
- await timeout()
-
const sendAmountFieldInputInEdit = sendAmountFieldInEdit.find('input:text')
sendAmountFieldInputInEdit.val('1.0')
reactTriggerChange(sendAmountFieldInputInEdit[0])
- await timeout()
-
- const sendButtonInEdit = $('.btn-clear.page-container__footer-button')
+ const sendButtonInEdit = await queryAsync($, '.btn-clear.page-container__footer-button')
assert.equal(sendButtonInEdit[0].textContent, 'Next', 'next button in edit rendered')
sendButtonInEdit[0].click()
- await timeout()
-
// TODO: Need a way to mock background so that we can test correct transition from editing to confirm
selectState.val('confirm new ui')
reactTriggerChange(selectState[0])
-
- await timeout(2000)
- const confirmScreenConfirmButton = $('.confirm-screen-confirm-button')
+ const confirmScreenConfirmButton = await queryAsync($, '.btn-confirm.page-container__footer-button')
console.log(`+++++++++++++++++++++++++++++++= confirmScreenConfirmButton[0]`, confirmScreenConfirmButton[0]);
confirmScreenConfirmButton[0].click()
- await timeout(2000)
-
- const txView = $('.tx-view')
+ const txView = await queryAsync($, '.tx-view')
console.log(`++++++++++++++++++++++++++++++++ txView[0]`, txView[0]);
assert.ok(txView[0], 'Should return to the account details screen after confirming')
}
-
-function timeout (time) {
- return new Promise((resolve, reject) => {
- setTimeout(resolve, time || 1500)
- })
-} \ No newline at end of file
diff --git a/test/lib/react-trigger-change.js b/test/lib/react-trigger-change.js
new file mode 100644
index 000000000..a25ddff00
--- /dev/null
+++ b/test/lib/react-trigger-change.js
@@ -0,0 +1,161 @@
+// Trigger React's synthetic change events on input, textarea and select elements
+// https://github.com/vitalyq/react-trigger-change
+
+/******************IMPORTANT NOTE******************/
+/* This file is a modification of the */
+/* 'react-trigger-change' library linked above. */
+/* That library breaks when 'onFocus' events are */
+/* added to components under test because it */
+/* dispatches focus events to ensure changes are */
+/* triggered in some versions of IE. */
+/* This modification removes the accomodations */
+/* 'react-trigger-change' makes for IE to ensure */
+/* our tests can pass in chrome and firefox. */
+/**************************************************/
+
+'use strict';
+
+// Constants and functions are declared inside the closure.
+// In this way, reactTriggerChange can be passed directly to executeScript in Selenium.
+module.exports = function reactTriggerChange(node) {
+ var supportedInputTypes = {
+ color: true,
+ date: true,
+ datetime: true,
+ 'datetime-local': true,
+ email: true,
+ month: true,
+ number: true,
+ password: true,
+ range: true,
+ search: true,
+ tel: true,
+ text: true,
+ time: true,
+ url: true,
+ week: true
+ };
+ var nodeName = node.nodeName.toLowerCase();
+ var type = node.type;
+ var event;
+ var descriptor;
+ var initialValue;
+ var initialChecked;
+ var initialCheckedRadio;
+
+ // Do not try to delete non-configurable properties.
+ // Value and checked properties on DOM elements are non-configurable in PhantomJS.
+ function deletePropertySafe(elem, prop) {
+ var desc = Object.getOwnPropertyDescriptor(elem, prop);
+ if (desc && desc.configurable) {
+ delete elem[prop];
+ }
+ }
+
+ function getCheckedRadio(radio) {
+ var name = radio.name;
+ var radios;
+ var i;
+ if (name) {
+ radios = document.querySelectorAll('input[type="radio"][name="' + name + '"]');
+ for (i = 0; i < radios.length; i += 1) {
+ if (radios[i].checked) {
+ return radios[i] !== radio ? radios[i] : null;
+ }
+ }
+ }
+ return null;
+ }
+
+ function preventChecking(e) {
+ e.preventDefault();
+ if (!initialChecked) {
+ e.target.checked = false;
+ }
+ if (initialCheckedRadio) {
+ initialCheckedRadio.checked = true;
+ }
+ }
+
+ if (nodeName === 'select' ||
+ (nodeName === 'input' && type === 'file')) {
+ // IE9-IE11, non-IE
+ // Dispatch change.
+ event = document.createEvent('HTMLEvents');
+ event.initEvent('change', true, false);
+ node.dispatchEvent(event);
+ } else if ((nodeName === 'input' && supportedInputTypes[type]) ||
+ nodeName === 'textarea') {
+ // React 16
+ // Cache artificial value property descriptor.
+ // Property doesn't exist in React <16, descriptor is undefined.
+ descriptor = Object.getOwnPropertyDescriptor(node, 'value');
+
+ // Update inputValueTracking cached value.
+ // Remove artificial value property.
+ // Restore initial value to trigger event with it.
+ initialValue = node.value;
+ node.value = initialValue + '#';
+ deletePropertySafe(node, 'value');
+ node.value = initialValue;
+
+ // React 0.14: IE10-IE11, non-IE
+ // React 15: non-IE
+ // React 16: IE10-IE11, non-IE
+ event = document.createEvent('HTMLEvents');
+ event.initEvent('input', true, false);
+ node.dispatchEvent(event);
+
+ // React 16
+ // Restore artificial value property descriptor.
+ if (descriptor) {
+ Object.defineProperty(node, 'value', descriptor);
+ }
+ } else if (nodeName === 'input' && type === 'checkbox') {
+ // Invert inputValueTracking cached value.
+ node.checked = !node.checked;
+
+ // Dispatch click.
+ // Click event inverts checked value.
+ event = document.createEvent('MouseEvents');
+ event.initEvent('click', true, true);
+ node.dispatchEvent(event);
+ } else if (nodeName === 'input' && type === 'radio') {
+ // Cache initial checked value.
+ initialChecked = node.checked;
+
+ // Find and cache initially checked radio in the group.
+ initialCheckedRadio = getCheckedRadio(node);
+
+ // React 16
+ // Cache property descriptor.
+ // Invert inputValueTracking cached value.
+ // Remove artificial checked property.
+ // Restore initial value, otherwise preventDefault will eventually revert the value.
+ descriptor = Object.getOwnPropertyDescriptor(node, 'checked');
+ node.checked = !initialChecked;
+ deletePropertySafe(node, 'checked');
+ node.checked = initialChecked;
+
+ // Prevent toggling during event capturing phase.
+ // Set checked value to false if initialChecked is false,
+ // otherwise next listeners will see true.
+ // Restore initially checked radio in the group.
+ node.addEventListener('click', preventChecking, true);
+
+ // Dispatch click.
+ // Click event inverts checked value.
+ event = document.createEvent('MouseEvents');
+ event.initEvent('click', true, true);
+ node.dispatchEvent(event);
+
+ // Remove listener to stop further change prevention.
+ node.removeEventListener('click', preventChecking, true);
+
+ // React 16
+ // Restore artificial checked property descriptor.
+ if (descriptor) {
+ Object.defineProperty(node, 'checked', descriptor);
+ }
+ }
+};
diff --git a/test/lib/util.js b/test/lib/util.js
new file mode 100644
index 000000000..626280745
--- /dev/null
+++ b/test/lib/util.js
@@ -0,0 +1,53 @@
+module.exports = {
+ timeout,
+ queryAsync,
+ findAsync,
+ pollUntilTruthy,
+}
+
+function timeout (time) {
+ return new Promise((resolve, reject) => {
+ setTimeout(resolve, time || 1500)
+ })
+}
+
+async function findAsync(container, selector, opts) {
+ try {
+ return await pollUntilTruthy(() => {
+ const result = container.find(selector)
+ if (result.length > 0) return result
+ }, opts)
+ } catch (err) {
+ throw new Error(`Failed to find element within interval: "${selector}"`)
+ }
+}
+
+async function queryAsync(jQuery, selector, opts) {
+ try {
+ return await pollUntilTruthy(() => {
+ const result = jQuery(selector)
+ if (result.length > 0) return result
+ }, opts)
+ } catch (err) {
+ throw new Error(`Failed to find element within interval: "${selector}"`)
+ }
+}
+
+async function pollUntilTruthy(fn, opts = {}){
+ const pollingInterval = opts.pollingInterval || 100
+ const timeoutInterval = opts.timeoutInterval || 5000
+ const start = Date.now()
+ let result
+ while (!result) {
+ // check if timedout
+ const now = Date.now()
+ if ((now - start) > timeoutInterval) {
+ throw new Error(`pollUntilTruthy - failed to return truthy within interval`)
+ }
+ // check for result
+ result = fn()
+ // run again after timeout
+ await timeout(pollingInterval, timeoutInterval)
+ }
+ return result
+}
diff --git a/test/unit/components/binary-renderer-test.js b/test/unit/components/binary-renderer-test.js
index ee2fa8b60..7bf9250cc 100644
--- a/test/unit/components/binary-renderer-test.js
+++ b/test/unit/components/binary-renderer-test.js
@@ -1,5 +1,5 @@
var assert = require('assert')
-var BinaryRenderer = require('../../../ui/app/components/binary-renderer')
+var BinaryRenderer = require('../../../old-ui/app/components/binary-renderer')
describe('BinaryRenderer', function () {
let binaryRenderer
diff --git a/test/unit/components/bn-as-decimal-input-test.js b/test/unit/components/bn-as-decimal-input-test.js
index 58ecc9c89..7b9d9814f 100644
--- a/test/unit/components/bn-as-decimal-input-test.js
+++ b/test/unit/components/bn-as-decimal-input-test.js
@@ -6,7 +6,7 @@ const ReactTestUtils = require('react-addons-test-utils')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
-var BnInput = require('../../../ui/app/components/bn-as-decimal-input')
+var BnInput = require('../../../old-ui/app/components/bn-as-decimal-input')
describe('BnInput', function () {
it('can tolerate a gas decimal number at a high precision', function (done) {
diff --git a/test/unit/development/version–bump-test.js b/test/unit/development/version–bump-test.js
index 1c445c8b4..5e37d4410 100644
--- a/test/unit/development/version–bump-test.js
+++ b/test/unit/development/version–bump-test.js
@@ -1,6 +1,6 @@
const assert = require('assert')
const versionBump = require('../../../development/version-bump')
-const { promisify } = require('util')
+const promisify = require('pify')
const fs = require('fs')
const readFile = promisify(fs.readFile)
const path = require('path')
@@ -41,5 +41,3 @@ describe('version bumper', function () {
assert.ok(result.changelog.includes(expected))
})
})
-
-
diff --git a/test/unit/nameForAccount_test.js b/test/unit/nameForAccount_test.js
index e7c0b18b4..32af49e9d 100644
--- a/test/unit/nameForAccount_test.js
+++ b/test/unit/nameForAccount_test.js
@@ -2,7 +2,7 @@ var assert = require('assert')
var sinon = require('sinon')
var path = require('path')
-var contractNamer = require(path.join(__dirname, '..', '..', 'ui', 'lib', 'contract-namer.js'))
+var contractNamer = require(path.join(__dirname, '..', '..', 'old-ui', 'lib', 'contract-namer.js'))
describe('contractNamer', function () {
beforeEach(function () {
diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js
index cc99afee4..712097fce 100644
--- a/test/unit/tx-controller-test.js
+++ b/test/unit/tx-controller-test.js
@@ -392,6 +392,49 @@ describe('Transaction Controller', function () {
})
})
+ describe('#retryTransaction', function () {
+ it('should create a new txMeta with the same txParams as the original one', function (done) {
+ let txParams = {
+ nonce: '0x00',
+ from: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4',
+ to: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4',
+ data: '0x0',
+ }
+ txController.txStateManager._saveTxList([
+ { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams },
+ ])
+ txController.retryTransaction(1)
+ .then((txMeta) => {
+ assert.equal(txMeta.txParams.nonce, txParams.nonce, 'nonce should be the same')
+ assert.equal(txMeta.txParams.from, txParams.from, 'from should be the same')
+ assert.equal(txMeta.txParams.to, txParams.to, 'to should be the same')
+ assert.equal(txMeta.txParams.data, txParams.data, 'data should be the same')
+ assert.ok(('lastGasPrice' in txMeta), 'should have the key `lastGasPrice`')
+ assert.equal(txController.txStateManager.getTxList().length, 2)
+ done()
+ }).catch(done)
+ })
+ })
+
+ describe('#_markNonceDuplicatesDropped', function () {
+ it('should mark all nonce duplicates as dropped without marking the confirmed transaction as dropped', function () {
+ txController.txStateManager._saveTxList([
+ { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ { id: 2, status: 'submitted', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ { id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ { id: 4, status: 'submitted', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ { id: 5, status: 'submitted', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ { id: 6, status: 'submitted', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ { id: 7, status: 'submitted', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ ])
+ txController._markNonceDuplicatesDropped(1)
+ const confirmedTx = txController.txStateManager.getTx(1)
+ const droppedTxs = txController.txStateManager.getFilteredTxList({ nonce: '0x01', status: 'dropped' })
+ assert.equal(confirmedTx.status, 'confirmed', 'the confirmedTx should remain confirmed')
+ assert.equal(droppedTxs.length, 6, 'their should be 6 dropped txs')
+
+ })
+ })
describe('#getPendingTransactions', function () {
beforeEach(function () {
@@ -401,7 +444,7 @@ describe('Transaction Controller', function () {
{ id: 3, status: 'approved', metamaskNetworkId: currentNetworkId, txParams: {} },
{ id: 4, status: 'signed', metamaskNetworkId: currentNetworkId, txParams: {} },
{ id: 5, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {} },
- { id: 6, status: 'confimed', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 6, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} },
{ id: 7, status: 'failed', metamaskNetworkId: currentNetworkId, txParams: {} },
])
})
diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js
deleted file mode 100644
index 0da435298..000000000
--- a/ui/app/account-detail.js
+++ /dev/null
@@ -1,117 +0,0 @@
-const inherits = require('util').inherits
-const extend = require('xtend')
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-const actions = require('./actions')
-const valuesFor = require('./util').valuesFor
-const TransactionList = require('./components/transaction-list')
-const ExportAccountView = require('./components/account-export')
-const TabBar = require('./components/tab-bar')
-const TokenList = require('./components/token-list')
-
-module.exports = connect(mapStateToProps)(AccountDetailScreen)
-
-function mapStateToProps (state) {
- return {
- metamask: state.metamask,
- identities: state.metamask.identities,
- accounts: state.metamask.accounts,
- address: state.metamask.selectedAddress,
- accountDetail: state.appState.accountDetail,
- network: state.metamask.network,
- unapprovedMsgs: valuesFor(state.metamask.unapprovedMsgs),
- shapeShiftTxList: state.metamask.shapeShiftTxList,
- transactions: state.metamask.selectedAddressTxList || [],
- conversionRate: state.metamask.conversionRate,
- currentCurrency: state.metamask.currentCurrency,
- currentAccountTab: state.metamask.currentAccountTab,
- tokens: state.metamask.tokens,
- computedBalances: state.metamask.computedBalances,
- }
-}
-
-inherits(AccountDetailScreen, Component)
-function AccountDetailScreen () {
- Component.call(this)
-}
-
-// Note: This component is no longer used. Leaving the file for reference:
-// - structuring routing for add token
-// - state required for TxList
-// Delete file when those features are complete
-AccountDetailScreen.prototype.render = function () {}
-
-AccountDetailScreen.prototype.subview = function () {
- var subview
- try {
- subview = this.props.accountDetail.subview
- } catch (e) {
- subview = null
- }
-
- switch (subview) {
- case 'transactions':
- return this.tabSections()
- case 'export':
- var state = extend({key: 'export'}, this.props)
- return h(ExportAccountView, state)
- default:
- return this.tabSections()
- }
-}
-
-AccountDetailScreen.prototype.tabSections = function () {
- const { currentAccountTab } = this.props
-
- return h('section.tabSection.full-flex-height.grow-tenx', [
-
- h(TabBar, {
- tabs: [
- { content: 'Sent', key: 'history' },
- { content: 'Tokens', key: 'tokens' },
- ],
- defaultTab: currentAccountTab || 'history',
- tabSelected: (key) => {
- this.props.dispatch(actions.setCurrentAccountTab(key))
- },
- }),
-
- this.tabSwitchView(),
- ])
-}
-
-AccountDetailScreen.prototype.tabSwitchView = function () {
- const props = this.props
- const { address, network } = props
- const { currentAccountTab, tokens } = this.props
-
- switch (currentAccountTab) {
- case 'tokens':
- return h(TokenList, {
- userAddress: address,
- network,
- tokens,
- addToken: () => this.props.dispatch(actions.showAddTokenPage()),
- })
- default:
- return this.transactionList()
- }
-}
-
-AccountDetailScreen.prototype.transactionList = function () {
- const {transactions, unapprovedMsgs, address,
- network, shapeShiftTxList, conversionRate } = this.props
-
- return h(TransactionList, {
- transactions: transactions.sort((a, b) => b.time - a.time),
- network,
- unapprovedMsgs,
- conversionRate,
- address,
- shapeShiftTxList,
- viewPendingTx: (txId) => {
- this.props.dispatch(actions.viewPendingTx(txId))
- },
- })
-}
diff --git a/ui/app/accounts/import/index.js b/ui/app/accounts/import/index.js
index adb52db74..fc9031a65 100644
--- a/ui/app/accounts/import/index.js
+++ b/ui/app/accounts/import/index.js
@@ -2,6 +2,7 @@ const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
+const t = require('../../../i18n')
import Select from 'react-select'
// Subviews
@@ -9,8 +10,8 @@ const JsonImportView = require('./json.js')
const PrivateKeyImportView = require('./private-key.js')
const menuItems = [
- 'Private Key',
- 'JSON File',
+ t('privateKey'),
+ t('jsonFile'),
]
module.exports = connect(mapStateToProps)(AccountImportSubview)
@@ -36,7 +37,7 @@ AccountImportSubview.prototype.render = function () {
h('div.new-account-import-form', [
h('.new-account-import-disclaimer', [
- h('span', 'Imported accounts will not be associated with your originally created MetaMask account seedphrase. Learn more about imported accounts '),
+ h('span', t('importAccountMsg')),
h('span', {
style: {
cursor: 'pointer',
@@ -47,12 +48,12 @@ AccountImportSubview.prototype.render = function () {
url: 'https://metamask.helpscoutdocs.com/article/17-what-are-loose-accounts',
})
},
- }, 'here'),
+ }, t('here')),
]),
h('div.new-account-import-form__select-section', [
- h('div.new-account-import-form__select-label', 'Select Type'),
+ h('div.new-account-import-form__select-label', t('selectType')),
h(Select, {
className: 'new-account-import-form__select',
@@ -85,9 +86,9 @@ AccountImportSubview.prototype.renderImportView = function () {
const current = type || menuItems[0]
switch (current) {
- case 'Private Key':
+ case t('privateKey'):
return h(PrivateKeyImportView)
- case 'JSON File':
+ case t('jsonFile'):
return h(JsonImportView)
default:
return h(JsonImportView)
diff --git a/ui/app/accounts/import/json.js b/ui/app/accounts/import/json.js
index c13622166..fa25168f1 100644
--- a/ui/app/accounts/import/json.js
+++ b/ui/app/accounts/import/json.js
@@ -4,6 +4,7 @@ const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../actions')
const FileInput = require('react-simple-file-input').default
+const t = require('../../../i18n')
const HELP_LINK = 'https://support.metamask.io/kb/article/7-importing-accounts'
@@ -23,11 +24,11 @@ class JsonImportSubview extends Component {
return (
h('div.new-account-import-form__json', [
- h('p', 'Used by a variety of different clients'),
+ h('p', t('usedByClients')),
h('a.warning', {
href: HELP_LINK,
target: '_blank',
- }, 'File import not working? Click here!'),
+ }, t('fileImportFail')),
h(FileInput, {
readAs: 'text',
@@ -42,7 +43,7 @@ class JsonImportSubview extends Component {
h('input.new-account-import-form__input-password', {
type: 'password',
- placeholder: 'Enter password',
+ placeholder: t('enterPassword'),
id: 'json-password-box',
onKeyPress: this.createKeyringOnEnter.bind(this),
}),
@@ -52,13 +53,13 @@ class JsonImportSubview extends Component {
h('button.new-account-create-form__button-cancel', {
onClick: () => this.props.goHome(),
}, [
- 'CANCEL',
+ t('cancel'),
]),
h('button.new-account-create-form__button-create', {
onClick: () => this.createNewKeychain(),
}, [
- 'IMPORT',
+ t('import'),
]),
]),
@@ -83,14 +84,14 @@ class JsonImportSubview extends Component {
const state = this.state
if (!state) {
- const message = 'You must select a valid file to import.'
+ const message = t('validFileImport')
return this.props.displayWarning(message)
}
const { fileContents } = state
if (!fileContents) {
- const message = 'You must select a file to import.'
+ const message = t('needImportFile')
return this.props.displayWarning(message)
}
@@ -98,7 +99,7 @@ class JsonImportSubview extends Component {
const password = passwordInput.value
if (!password) {
- const message = 'You must enter a password for the selected file.'
+ const message = t('needImportPassword')
return this.props.displayWarning(message)
}
diff --git a/ui/app/accounts/import/private-key.js b/ui/app/accounts/import/private-key.js
index 12f3a6430..bc9e9384e 100644
--- a/ui/app/accounts/import/private-key.js
+++ b/ui/app/accounts/import/private-key.js
@@ -3,6 +3,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../actions')
+const t = require('../../../i18n')
module.exports = connect(mapStateToProps, mapDispatchToProps)(PrivateKeyImportView)
@@ -33,9 +34,9 @@ PrivateKeyImportView.prototype.render = function () {
return (
h('div.new-account-import-form__private-key', [
- h('div.new-account-import-form__private-key-password-container', [
+ h('span.new-account-create-form__instruction', t('pastePrivateKey')),
- h('span.new-account-import-form__instruction', 'Paste your private key string here:'),
+ h('div.new-account-import-form__private-key-password-container', [
h('input.new-account-import-form__input-password', {
type: 'password',
@@ -47,16 +48,16 @@ PrivateKeyImportView.prototype.render = function () {
h('div.new-account-import-form__buttons', {}, [
- h('button.new-account-create-form__button-cancel', {
+ h('button.new-account-create-form__button-cancel.allcaps', {
onClick: () => goHome(),
}, [
- 'CANCEL',
+ t('cancel'),
]),
- h('button.new-account-create-form__button-create', {
+ h('button.new-account-create-form__button-create.allcaps', {
onClick: () => this.createNewKeychain(),
}, [
- 'IMPORT',
+ t('import'),
]),
]),
diff --git a/ui/app/accounts/import/seed.js b/ui/app/accounts/import/seed.js
index b4a7c0afa..9ffc669a2 100644
--- a/ui/app/accounts/import/seed.js
+++ b/ui/app/accounts/import/seed.js
@@ -2,6 +2,7 @@ const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
+const t = require('../../../i18n')
module.exports = connect(mapStateToProps)(SeedImportSubview)
@@ -20,11 +21,10 @@ SeedImportSubview.prototype.render = function () {
style: {
},
}, [
- `Paste your seed phrase here!`,
+ t('pasteSeed'),
h('textarea'),
h('br'),
- h('button', 'Submit'),
+ h('button', t('submit')),
])
)
}
-
diff --git a/ui/app/accounts/new-account/create-form.js b/ui/app/accounts/new-account/create-form.js
index a6b3bba4b..8ef842a2a 100644
--- a/ui/app/accounts/new-account/create-form.js
+++ b/ui/app/accounts/new-account/create-form.js
@@ -3,6 +3,7 @@ const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const { connect } = require('react-redux')
const actions = require('../../actions')
+const t = require('../../../i18n')
class NewAccountCreateForm extends Component {
constructor (props) {
@@ -13,7 +14,7 @@ class NewAccountCreateForm extends Component {
this.state = {
newAccountName: '',
- defaultAccountName: `Account ${newAccountNumber}`,
+ defaultAccountName: t('newAccountNumberName', [newAccountNumber]),
}
}
@@ -24,7 +25,7 @@ class NewAccountCreateForm extends Component {
return h('div.new-account-create-form', [
h('div.new-account-create-form__input-label', {}, [
- 'Account Name',
+ t('accountName'),
]),
h('div.new-account-create-form__input-wrapper', {}, [
@@ -37,16 +38,16 @@ class NewAccountCreateForm extends Component {
h('div.new-account-create-form__buttons', {}, [
- h('button.new-account-create-form__button-cancel', {
+ h('button.new-account-create-form__button-cancel.allcaps', {
onClick: () => this.props.goHome(),
}, [
- 'CANCEL',
+ t('cancel'),
]),
- h('button.new-account-create-form__button-create', {
+ h('button.new-account-create-form__button-create.allcaps', {
onClick: () => this.props.createAccount(newAccountName || defaultAccountName),
}, [
- 'CREATE',
+ t('create'),
]),
]),
diff --git a/ui/app/accounts/new-account/index.js b/ui/app/accounts/new-account/index.js
index acf0dc6e4..854568c77 100644
--- a/ui/app/accounts/new-account/index.js
+++ b/ui/app/accounts/new-account/index.js
@@ -3,6 +3,7 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
+const t = require('../../../i18n')
const { getCurrentViewContext } = require('../../selectors')
const classnames = require('classnames')
@@ -42,10 +43,10 @@ AccountDetailsModal.prototype.render = function () {
const { displayedForm, displayForm } = this.props
return h('div.new-account', {}, [
-
+
h('div.new-account__header', [
- h('div.new-account__title', 'New Account'),
+ h('div.new-account__title', t('newAccount')),
h('div.new-account__tabs', [
@@ -55,7 +56,7 @@ AccountDetailsModal.prototype.render = function () {
'new-account__tabs__unselected cursor-pointer': displayedForm !== 'CREATE',
}),
onClick: () => displayForm('CREATE'),
- }, 'Create'),
+ }, t('createDen')),
h('div.new-account__tabs__tab', {
className: classnames('new-account__tabs__tab', {
@@ -63,7 +64,7 @@ AccountDetailsModal.prototype.render = function () {
'new-account__tabs__unselected cursor-pointer': displayedForm !== 'IMPORT',
}),
onClick: () => displayForm('IMPORT'),
- }, 'Import'),
+ }, t('import')),
]),
diff --git a/ui/app/actions.js b/ui/app/actions.js
index b56265edc..bc7ee3d07 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -75,6 +75,8 @@ var actions = {
resetAccount,
showNewVaultSeed: showNewVaultSeed,
showInfoPage: showInfoPage,
+ CLOSE_WELCOME_SCREEN: 'CLOSE_WELCOME_SCREEN',
+ closeWelcomeScreen,
// seed recovery actions
REVEAL_SEED_CONFIRMATION: 'REVEAL_SEED_CONFIRMATION',
revealSeedConfirmation: revealSeedConfirmation,
@@ -788,7 +790,7 @@ function updateTransaction (txData) {
function updateAndApproveTx (txData) {
log.info('actions: updateAndApproveTx: ' + JSON.stringify(txData))
return (dispatch) => {
- log.debug(`actions calling background.updateAndApproveTx`)
+ log.debug(`actions calling background.updateAndApproveTx.`)
background.updateAndApproveTransaction(txData, (err) => {
dispatch(actions.hideLoadingIndication())
dispatch(actions.updateTransactionParams(txData.id, txData.txParams))
@@ -947,6 +949,12 @@ function showNewVaultSeed (seed) {
}
}
+function closeWelcomeScreen () {
+ return {
+ type: actions.CLOSE_WELCOME_SCREEN,
+ }
+}
+
function backToUnlockView () {
return {
type: actions.BACK_TO_UNLOCK_VIEW,
@@ -1270,8 +1278,10 @@ function retryTransaction (txId) {
if (err) {
return dispatch(actions.displayWarning(err.message))
}
+ const { selectedAddressTxList } = newState
+ const { id: newTxId } = selectedAddressTxList[selectedAddressTxList.length - 1]
dispatch(actions.updateMetamaskState(newState))
- dispatch(actions.viewPendingTx(txId))
+ dispatch(actions.viewPendingTx(newTxId))
})
}
}
diff --git a/ui/app/add-token.js b/ui/app/add-token.js
index b8878b772..b3a5bdc20 100644
--- a/ui/app/add-token.js
+++ b/ui/app/add-token.js
@@ -26,6 +26,7 @@ const fuse = new Fuse(contractList, {
const actions = require('./actions')
const ethUtil = require('ethereumjs-util')
const { tokenInfoGetter } = require('./token-util')
+const t = require('../i18n')
const emptyAddr = '0x0000000000000000000000000000000000000000'
@@ -139,28 +140,28 @@ AddTokenScreen.prototype.validate = function () {
if (customAddress) {
const validAddress = ethUtil.isValidAddress(customAddress)
if (!validAddress) {
- errors.customAddress = 'Address is invalid. '
+ errors.customAddress = t('invalidAddress')
}
const validDecimals = customDecimals !== null && customDecimals >= 0 && customDecimals < 36
if (!validDecimals) {
- errors.customDecimals = 'Decimals must be at least 0, and not over 36.'
+ errors.customDecimals = t('decimalsMustZerotoTen')
}
const symbolLen = customSymbol.trim().length
const validSymbol = symbolLen > 0 && symbolLen < 10
if (!validSymbol) {
- errors.customSymbol = 'Symbol must be between 0 and 10 characters.'
+ errors.customSymbol = t('symbolBetweenZeroTen')
}
const ownAddress = identitiesList.includes(standardAddress)
if (ownAddress) {
- errors.customAddress = 'Personal address detected. Input the token contract address.'
+ errors.customAddress = t('personalAddressDetected')
}
const tokenAlreadyAdded = this.checkExistingAddresses(customAddress)
if (tokenAlreadyAdded) {
- errors.customAddress = 'Token has already been added.'
+ errors.customAddress = t('tokenAlreadyAdded')
}
} else if (
Object.entries(selectedTokens)
@@ -168,7 +169,7 @@ AddTokenScreen.prototype.validate = function () {
isEmpty && !isSelected
), true)
) {
- errors.tokenSelector = 'Must select at least 1 token.'
+ errors.tokenSelector = t('mustSelectOne')
}
return {
@@ -198,7 +199,7 @@ AddTokenScreen.prototype.renderCustomForm = function () {
'add-token__add-custom-field--error': errors.customAddress,
}),
}, [
- h('div.add-token__add-custom-label', 'Token Address'),
+ h('div.add-token__add-custom-label', t('tokenAddress')),
h('input.add-token__add-custom-input', {
type: 'text',
onChange: this.tokenAddressDidChange,
@@ -211,7 +212,7 @@ AddTokenScreen.prototype.renderCustomForm = function () {
'add-token__add-custom-field--error': errors.customSymbol,
}),
}, [
- h('div.add-token__add-custom-label', 'Token Symbol'),
+ h('div.add-token__add-custom-label', t('tokenSymbol')),
h('input.add-token__add-custom-input', {
type: 'text',
onChange: this.tokenSymbolDidChange,
@@ -225,7 +226,7 @@ AddTokenScreen.prototype.renderCustomForm = function () {
'add-token__add-custom-field--error': errors.customDecimals,
}),
}, [
- h('div.add-token__add-custom-label', 'Decimals of Precision'),
+ h('div.add-token__add-custom-label', t('decimal')),
h('input.add-token__add-custom-input', {
type: 'number',
onChange: this.tokenDecimalsDidChange,
@@ -299,11 +300,11 @@ AddTokenScreen.prototype.renderConfirmation = function () {
h('div.add-token', [
h('div.add-token__wrapper', [
h('div.add-token__title-container.add-token__confirmation-title', [
- h('div.add-token__title', 'Add Token'),
- h('div.add-token__description', 'Would you like to add these tokens?'),
+ h('div.add-token__title', t('addToken')),
+ h('div.add-token__description', t('likeToAddTokens')),
]),
h('div.add-token__content-container.add-token__confirmation-content', [
- h('div.add-token__description.add-token__confirmation-description', 'Your balances'),
+ h('div.add-token__description.add-token__confirmation-description', t('balances')),
h('div.add-token__confirmation-token-list',
Object.entries(tokens)
.map(([ address, token ]) => (
@@ -322,10 +323,10 @@ AddTokenScreen.prototype.renderConfirmation = function () {
h('div.add-token__buttons', [
h('button.btn-cancel.add-token__button', {
onClick: () => this.setState({ isShowingConfirmation: false }),
- }, 'Back'),
+ }, t('back')),
h('button.btn-clear.add-token__button', {
onClick: () => addTokens(tokens).then(goHome),
- }, 'Add Tokens'),
+ }, t('addTokens')),
]),
])
)
@@ -341,15 +342,15 @@ AddTokenScreen.prototype.render = function () {
h('div.add-token', [
h('div.add-token__wrapper', [
h('div.add-token__title-container', [
- h('div.add-token__title', 'Add Token'),
- h('div.add-token__description', 'Keep track of the tokens you’ve bought with your MetaMask account. If you bought tokens using a different account, those tokens will not appear here.'),
- h('div.add-token__description', 'Search for tokens or select from our list of popular tokens.'),
+ h('div.add-token__title', t('addToken')),
+ h('div.add-token__description', t('tokenWarning1')),
+ h('div.add-token__description', t('tokenSelection')),
]),
h('div.add-token__content-container', [
h('div.add-token__input-container', [
h('input.add-token__input', {
type: 'text',
- placeholder: 'Search',
+ placeholder: t('search'),
onChange: e => this.setState({ searchQuery: e.target.value }),
}),
h('div.add-token__search-input-error-message', errors.tokenSelector),
@@ -363,7 +364,7 @@ AddTokenScreen.prototype.render = function () {
h('div.add-token__add-custom', {
onClick: () => this.setState({ isCollapsed: !isCollapsed }),
}, [
- 'Add custom token',
+ t('addCustomToken'),
h(`i.fa.fa-angle-${isCollapsed ? 'down' : 'up'}`),
]),
this.renderCustomForm(),
@@ -372,10 +373,10 @@ AddTokenScreen.prototype.render = function () {
h('div.add-token__buttons', [
h('button.btn-cancel.add-token__button', {
onClick: goHome,
- }, 'Cancel'),
+ }, t('cancel')),
h('button.btn-clear.add-token__button', {
onClick: this.onNext,
- }, 'Next'),
+ }, t('next')),
]),
])
)
diff --git a/ui/app/app.js b/ui/app/app.js
index 4e6da24c3..6d9296131 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -4,6 +4,7 @@ const connect = require('react-redux').connect
const h = require('react-hyperscript')
const actions = require('./actions')
const classnames = require('classnames')
+const t = require('../i18n')
// mascara
const MascaraFirstTime = require('../../mascara/src/app/first-time').default
@@ -12,6 +13,8 @@ const MascaraBuyEtherScreen = require('../../mascara/src/app/first-time/buy-ethe
const OldUIInitializeMenuScreen = require('./first-time/init-menu')
const InitializeMenuScreen = MascaraFirstTime
const NewKeyChainScreen = require('./new-keychain')
+const WelcomeScreen = require('./welcome-screen').default
+
// accounts
const MainContainer = require('./main-container')
const SendTransactionScreen2 = require('./components/send/send-v2-container')
@@ -91,6 +94,7 @@ function mapStateToProps (state) {
betaUI: state.metamask.featureFlags.betaUI,
isRevealingSeedWords: state.metamask.isRevealingSeedWords,
Qr: state.appState.Qr,
+ welcomeScreenSeen: state.metamask.welcomeScreenSeen,
// state needed to get account dropdown temporarily rendering from app bar
identities,
@@ -128,7 +132,7 @@ App.prototype.render = function () {
} = props
const isLoadingNetwork = network === 'loading' && props.currentView.name !== 'config'
const loadMessage = loadingMessage || isLoadingNetwork ?
- `Connecting to ${this.getNetworkName()}` : null
+ this.getConnectingLabel() : null
log.debug('Main ui render function')
return (
@@ -244,6 +248,7 @@ App.prototype.renderAppBar = function () {
isInitialized,
betaUI,
isPopup,
+ welcomeScreenSeen,
} = this.props
if (window.METAMASK_UI_TYPE === 'notification') {
@@ -269,7 +274,7 @@ App.prototype.renderAppBar = function () {
style: {},
}, [
- h('.app-header.flex-row.flex-space-between', {
+ (isInitialized || welcomeScreenSeen || isPopup || !betaUI) && h('.app-header.flex-row.flex-space-between', {
className: classnames({
'app-header--initialized': !isOnboarding,
}),
@@ -289,12 +294,12 @@ App.prototype.renderAppBar = function () {
// metamask name
h('.flex-row', [
- h('h1', 'MetaMask'),
- h('div.beta-label', 'BETA'),
+ h('h1', t('appName')),
+ h('div.beta-label', t('beta')),
]),
]),
- h('div.header__right-actions', [
+ betaUI && isInitialized && h('div.header__right-actions', [
h('div.network-component-wrapper', {
style: {},
}, [
@@ -324,8 +329,14 @@ App.prototype.renderAppBar = function () {
]),
]),
- !isInitialized && !isPopup && betaUI && h('h2.alpha-warning',
- 'Please be aware that this version is still under development'),
+ !isInitialized && !isPopup && betaUI && h('.alpha-warning__container', {}, [
+ h('h2', {
+ className: classnames({
+ 'alpha-warning': welcomeScreenSeen,
+ 'alpha-warning-welcome-screen': !welcomeScreenSeen,
+ }),
+ }, 'Please be aware that this version is still under development'),
+ ]),
])
)
@@ -369,11 +380,18 @@ App.prototype.renderPrimary = function () {
isOnboarding,
betaUI,
isRevealingSeedWords,
+ welcomeScreenSeen,
Qr,
+ isInitialized,
+ isUnlocked,
} = props
const isMascaraOnboarding = isMascara && isOnboarding
const isBetaUIOnboarding = betaUI && isOnboarding && !props.isPopup && !isRevealingSeedWords
+ if (!welcomeScreenSeen && betaUI && !isInitialized && !isUnlocked) {
+ return h(WelcomeScreen)
+ }
+
if (isMascaraOnboarding || isBetaUIOnboarding) {
return h(MascaraFirstTime)
}
@@ -532,6 +550,27 @@ App.prototype.toggleMetamaskActive = function () {
}
}
+App.prototype.getConnectingLabel = function () {
+ const { provider } = this.props
+ const providerName = provider.type
+
+ let name
+
+ if (providerName === 'mainnet') {
+ name = t('connectingToMainnet')
+ } else if (providerName === 'ropsten') {
+ name = t('connectingToRopsten')
+ } else if (providerName === 'kovan') {
+ name = t('connectingToRopsten')
+ } else if (providerName === 'rinkeby') {
+ name = t('connectingToRinkeby')
+ } else {
+ name = t('connectingToUnknown')
+ }
+
+ return name
+}
+
App.prototype.getNetworkName = function () {
const { provider } = this.props
const providerName = provider.type
@@ -539,15 +578,15 @@ App.prototype.getNetworkName = function () {
let name
if (providerName === 'mainnet') {
- name = 'Main Ethereum Network'
+ name = t('mainnet')
} else if (providerName === 'ropsten') {
- name = 'Ropsten Test Network'
+ name = t('ropsten')
} else if (providerName === 'kovan') {
- name = 'Kovan Test Network'
+ name = t('kovan')
} else if (providerName === 'rinkeby') {
- name = 'Rinkeby Test Network'
+ name = t('rinkeby')
} else {
- name = 'Unknown Private Network'
+ name = t('unknownNetwork')
}
return name
diff --git a/ui/app/components/account-dropdowns.js b/ui/app/components/account-dropdowns.js
index 1cd7a0847..1612d7b6a 100644
--- a/ui/app/components/account-dropdowns.js
+++ b/ui/app/components/account-dropdowns.js
@@ -9,6 +9,7 @@ const DropdownMenuItem = require('./dropdown').DropdownMenuItem
const Identicon = require('./identicon')
const ethUtil = require('ethereumjs-util')
const copyToClipboard = require('copy-to-clipboard')
+const t = require('../../i18n')
class AccountDropdowns extends Component {
constructor (props) {
@@ -79,7 +80,7 @@ class AccountDropdowns extends Component {
try { // Sometimes keyrings aren't loaded yet:
const type = keyring.type
const isLoose = type !== 'HD Key Tree'
- return isLoose ? h('.keyring-label', 'LOOSE') : null
+ return isLoose ? h('.keyring-label.allcaps', t('loose')) : null
} catch (e) { return }
}
@@ -129,7 +130,7 @@ class AccountDropdowns extends Component {
diameter: 32,
},
),
- h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, 'Create Account'),
+ h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, t('createAccount')),
],
),
h(
@@ -154,7 +155,7 @@ class AccountDropdowns extends Component {
fontSize: '24px',
marginBottom: '5px',
},
- }, 'Import Account'),
+ }, t('importAccount')),
]
),
]
@@ -192,7 +193,7 @@ class AccountDropdowns extends Component {
global.platform.openWindow({ url })
},
},
- 'View account on Etherscan',
+ t('etherscanView'),
),
h(
DropdownMenuItem,
@@ -204,7 +205,7 @@ class AccountDropdowns extends Component {
actions.showQrView(selected, identity ? identity.name : '')
},
},
- 'Show QR Code',
+ t('showQRCode'),
),
h(
DropdownMenuItem,
@@ -216,7 +217,7 @@ class AccountDropdowns extends Component {
copyToClipboard(checkSumAddress)
},
},
- 'Copy Address to clipboard',
+ t('copyAddress'),
),
h(
DropdownMenuItem,
@@ -226,7 +227,7 @@ class AccountDropdowns extends Component {
actions.requestAccountExport()
},
},
- 'Export Private Key',
+ t('exportPrivateKey'),
),
]
)
diff --git a/ui/app/components/account-export.js b/ui/app/components/account-export.js
index 32b103c86..5637bc8d0 100644
--- a/ui/app/components/account-export.js
+++ b/ui/app/components/account-export.js
@@ -6,6 +6,7 @@ const copyToClipboard = require('copy-to-clipboard')
const actions = require('../actions')
const ethUtil = require('ethereumjs-util')
const connect = require('react-redux').connect
+const t = require('../../i18n')
module.exports = connect(mapStateToProps)(ExportAccountView)
@@ -35,7 +36,7 @@ ExportAccountView.prototype.render = function () {
if (notExporting) return h('div')
if (exportRequested) {
- const warning = `Export private keys at your own risk.`
+ const warning = t('exportPrivateKeyWarning')
return (
h('div', {
style: {
@@ -53,7 +54,7 @@ ExportAccountView.prototype.render = function () {
h('p.error', warning),
h('input#exportAccount.sizing-input', {
type: 'password',
- placeholder: 'confirm password',
+ placeholder: t('confirmPassword').toLowerCase(),
onKeyPress: this.onExportKeyPress.bind(this),
style: {
position: 'relative',
@@ -74,10 +75,10 @@ ExportAccountView.prototype.render = function () {
style: {
marginRight: '10px',
},
- }, 'Submit'),
+ }, t('submit')),
h('button', {
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
- }, 'Cancel'),
+ }, t('cancel')),
]),
(this.props.warning) && (
h('span.error', {
@@ -98,7 +99,7 @@ ExportAccountView.prototype.render = function () {
margin: '0 20px',
},
}, [
- h('label', 'Your private key (click to copy):'),
+ h('label', t('copyPrivateKey') + ':'),
h('p.error.cursor-pointer', {
style: {
textOverflow: 'ellipsis',
@@ -112,13 +113,13 @@ ExportAccountView.prototype.render = function () {
}, plainKey),
h('button', {
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
- }, 'Done'),
+ }, t('done')),
h('button', {
style: {
marginLeft: '10px',
},
onClick: () => exportAsFile(`MetaMask ${nickname} Private Key`, plainKey),
- }, 'Save as File'),
+ }, t('saveAsFile')),
])
}
}
diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js
index 1a0103d4f..e838e8916 100644
--- a/ui/app/components/account-menu/index.js
+++ b/ui/app/components/account-menu/index.js
@@ -6,6 +6,7 @@ const actions = require('../../actions')
const { Menu, Item, Divider, CloseArea } = require('../dropdowns/components/menu')
const Identicon = require('../identicon')
const { formatBalance } = require('../../util')
+const t = require('../../../i18n')
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountMenu)
@@ -70,10 +71,10 @@ AccountMenu.prototype.render = function () {
h(Item, {
className: 'account-menu__header',
}, [
- 'My Accounts',
+ t('myAccounts'),
h('button.account-menu__logout-button', {
onClick: lockMetamask,
- }, 'Log out'),
+ }, t('logout')),
]),
h(Divider),
h('div.account-menu__accounts', this.renderAccounts()),
@@ -81,23 +82,23 @@ AccountMenu.prototype.render = function () {
h(Item, {
onClick: () => showNewAccountPage('CREATE'),
icon: h('img.account-menu__item-icon', { src: 'images/plus-btn-white.svg' }),
- text: 'Create Account',
+ text: t('createAccount'),
}),
h(Item, {
onClick: () => showNewAccountPage('IMPORT'),
icon: h('img.account-menu__item-icon', { src: 'images/import-account.svg' }),
- text: 'Import Account',
+ text: t('importAccount'),
}),
h(Divider),
h(Item, {
onClick: showInfoPage,
- icon: h('img.account-menu__item-icon', { src: 'images/mm-info-icon.svg' }),
- text: 'Info & Help',
+ icon: h('img', { src: 'images/mm-info-icon.svg' }),
+ text: t('infoHelp'),
}),
h(Item, {
onClick: showConfigPage,
icon: h('img.account-menu__item-icon', { src: 'images/settings.svg' }),
- text: 'Settings',
+ text: t('settings'),
}),
])
}
@@ -155,6 +156,6 @@ AccountMenu.prototype.indicateIfLoose = function (keyring) {
try { // Sometimes keyrings aren't loaded yet:
const type = keyring.type
const isLoose = type !== 'HD Key Tree'
- return isLoose ? h('.keyring-label', 'IMPORTED') : null
+ return isLoose ? h('.keyring-label.allcaps', t('imported')) : null
} catch (e) { return }
}
diff --git a/ui/app/components/balance.js b/ui/app/components/balance.js
deleted file mode 100644
index 57ca84564..000000000
--- a/ui/app/components/balance.js
+++ /dev/null
@@ -1,89 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const formatBalance = require('../util').formatBalance
-const generateBalanceObject = require('../util').generateBalanceObject
-const Tooltip = require('./tooltip.js')
-const FiatValue = require('./fiat-value.js')
-
-module.exports = EthBalanceComponent
-
-inherits(EthBalanceComponent, Component)
-function EthBalanceComponent () {
- Component.call(this)
-}
-
-EthBalanceComponent.prototype.render = function () {
- var props = this.props
- let { value } = props
- var style = props.style
- var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true
- value = value ? formatBalance(value, 6, needsParse) : '...'
- var width = props.width
-
- return (
-
- h('.ether-balance.ether-balance-amount', {
- style: style,
- }, [
- h('div', {
- style: {
- display: 'inline',
- width: width,
- },
- }, this.renderBalance(value)),
- ])
-
- )
-}
-EthBalanceComponent.prototype.renderBalance = function (value) {
- var props = this.props
- if (value === 'None') return value
- if (value === '...') return value
- var balanceObj = generateBalanceObject(value, props.shorten ? 1 : 3)
- var balance
- var splitBalance = value.split(' ')
- var ethNumber = splitBalance[0]
- var ethSuffix = splitBalance[1]
- const showFiat = 'showFiat' in props ? props.showFiat : true
-
- if (props.shorten) {
- balance = balanceObj.shortBalance
- } else {
- balance = balanceObj.balance
- }
-
- var label = balanceObj.label
-
- return (
- h(Tooltip, {
- position: 'bottom',
- title: `${ethNumber} ${ethSuffix}`,
- }, h('div.flex-column', [
- h('.flex-row', {
- style: {
- alignItems: 'flex-end',
- lineHeight: '13px',
- fontFamily: 'Montserrat Light',
- textRendering: 'geometricPrecision',
- },
- }, [
- h('div', {
- style: {
- width: '100%',
- textAlign: 'right',
- },
- }, this.props.incoming ? `+${balance}` : balance),
- h('div', {
- style: {
- color: ' #AEAEAE',
- fontSize: '12px',
- marginLeft: '5px',
- },
- }, label),
- ]),
-
- showFiat ? h(FiatValue, { value: props.value }) : null,
- ]))
- )
-}
diff --git a/ui/app/components/binary-renderer.js b/ui/app/components/binary-renderer.js
deleted file mode 100644
index 0b6a1f5c2..000000000
--- a/ui/app/components/binary-renderer.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const ethUtil = require('ethereumjs-util')
-const extend = require('xtend')
-
-module.exports = BinaryRenderer
-
-inherits(BinaryRenderer, Component)
-function BinaryRenderer () {
- Component.call(this)
-}
-
-BinaryRenderer.prototype.render = function () {
- const props = this.props
- const { value, style } = props
- const text = this.hexToText(value)
-
- const defaultStyle = extend({
- width: '315px',
- maxHeight: '210px',
- resize: 'none',
- border: 'none',
- background: 'white',
- padding: '3px',
- }, style)
-
- return (
- h('textarea.font-small', {
- readOnly: true,
- style: defaultStyle,
- defaultValue: text,
- })
- )
-}
-
-BinaryRenderer.prototype.hexToText = function (hex) {
- try {
- const stripped = ethUtil.stripHexPrefix(hex)
- const buff = Buffer.from(stripped, 'hex')
- return buff.toString('utf8')
- } catch (e) {
- return hex
- }
-}
-
diff --git a/ui/app/components/bn-as-decimal-input.js b/ui/app/components/bn-as-decimal-input.js
index 22e37602e..70701b039 100644
--- a/ui/app/components/bn-as-decimal-input.js
+++ b/ui/app/components/bn-as-decimal-input.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const extend = require('xtend')
+const t = require('../../i18n')
module.exports = BnAsDecimalInput
@@ -136,13 +137,13 @@ BnAsDecimalInput.prototype.constructWarning = function () {
let message = name ? name + ' ' : ''
if (min && max) {
- message += `must be greater than or equal to ${newMin} ${suffix} and less than or equal to ${newMax} ${suffix}.`
+ message += t('betweenMinAndMax', [`${newMin} ${suffix}`, `${newMax} ${suffix}`])
} else if (min) {
- message += `must be greater than or equal to ${newMin} ${suffix}.`
+ message += t('greaterThanMin', [`${newMin} ${suffix}`])
} else if (max) {
- message += `must be less than or equal to ${newMax} ${suffix}.`
+ message += t('lessThanMax', [`${newMax} ${suffix}`])
} else {
- message += 'Invalid input.'
+ message += t('invalidInput')
}
return message
diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js
index d5958787b..1e277a94b 100644
--- a/ui/app/components/buy-button-subview.js
+++ b/ui/app/components/buy-button-subview.js
@@ -9,6 +9,7 @@ const Loading = require('./loading')
const AccountPanel = require('./account-panel')
const RadioList = require('./custom-radio-list')
const networkNames = require('../../../app/scripts/config.js').networkNames
+const t = require('../../i18n')
module.exports = connect(mapStateToProps)(BuyButtonSubview)
@@ -76,7 +77,7 @@ BuyButtonSubview.prototype.headerSubview = function () {
paddingTop: '4px',
paddingBottom: '4px',
},
- }, 'Deposit Eth'),
+ }, t('depositEth')),
]),
// loading indication
@@ -118,7 +119,7 @@ BuyButtonSubview.prototype.headerSubview = function () {
paddingTop: '4px',
paddingBottom: '4px',
},
- }, 'Select Service'),
+ }, t('selectService')),
]),
])
@@ -143,7 +144,7 @@ BuyButtonSubview.prototype.primarySubview = function () {
case '4':
case '42':
const networkName = networkNames[network]
- const label = `${networkName} Test Faucet`
+ const label = `${networkName} ${t('testFaucet')}`
return (
h('div.flex-column', {
style: {
@@ -164,14 +165,14 @@ BuyButtonSubview.prototype.primarySubview = function () {
style: {
marginTop: '15px',
},
- }, 'Borrow With Dharma (Beta)')
+ }, t('borrowDharma'))
) : null,
])
)
default:
return (
- h('h2.error', 'Unknown network ID')
+ h('h2.error', t('unknownNetworkId'))
)
}
@@ -203,8 +204,8 @@ BuyButtonSubview.prototype.mainnetSubview = function () {
'ShapeShift',
],
subtext: {
- 'Coinbase': 'Crypto/FIAT (USA only)',
- 'ShapeShift': 'Crypto',
+ 'Coinbase': `${t('crypto')}/${t('fiat')} (${t('usaOnly')})`,
+ 'ShapeShift': t('crypto'),
},
onClick: this.radioHandler.bind(this),
}),
diff --git a/ui/app/components/coinbase-form.js b/ui/app/components/coinbase-form.js
index f70208625..e442b43d5 100644
--- a/ui/app/components/coinbase-form.js
+++ b/ui/app/components/coinbase-form.js
@@ -3,6 +3,7 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../actions')
+const t = require('../../i18n')
module.exports = connect(mapStateToProps)(CoinbaseForm)
@@ -37,11 +38,11 @@ CoinbaseForm.prototype.render = function () {
}, [
h('button.btn-green', {
onClick: this.toCoinbase.bind(this),
- }, 'Continue to Coinbase'),
+ }, t('continueToCoinbase')),
h('button.btn-red', {
onClick: () => props.dispatch(actions.goHome()),
- }, 'Cancel'),
+ }, t('cancel')),
]),
])
}
diff --git a/ui/app/components/copyButton.js b/ui/app/components/copyButton.js
index a25d0719c..355f78d45 100644
--- a/ui/app/components/copyButton.js
+++ b/ui/app/components/copyButton.js
@@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const copyToClipboard = require('copy-to-clipboard')
+const t = require('../../i18n')
const Tooltip = require('./tooltip')
@@ -22,7 +23,7 @@ CopyButton.prototype.render = function () {
const value = props.value
const copied = state.copied
- const message = copied ? 'Copied' : props.title || ' Copy '
+ const message = copied ? t('copiedButton') : props.title || t('copyButton')
return h('.copy-button', {
style: {
diff --git a/ui/app/components/copyable.js b/ui/app/components/copyable.js
index a4f6f4bc6..fca7d3863 100644
--- a/ui/app/components/copyable.js
+++ b/ui/app/components/copyable.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const Tooltip = require('./tooltip')
const copyToClipboard = require('copy-to-clipboard')
+const t = require('../../i18n')
module.exports = Copyable
@@ -22,7 +23,7 @@ Copyable.prototype.render = function () {
const { copied } = state
return h(Tooltip, {
- title: copied ? 'Copied!' : 'Copy',
+ title: copied ? t('copiedExclamation') : t('copy'),
position: 'bottom',
}, h('span', {
style: {
diff --git a/ui/app/components/currency-input.js b/ui/app/components/currency-input.js
index 6f7862e51..ece3eb43d 100644
--- a/ui/app/components/currency-input.js
+++ b/ui/app/components/currency-input.js
@@ -8,8 +8,12 @@ inherits(CurrencyInput, Component)
function CurrencyInput (props) {
Component.call(this)
+ const sanitizedValue = sanitizeValue(props.value)
+
this.state = {
- value: sanitizeValue(props.value),
+ value: sanitizedValue,
+ emptyState: false,
+ focused: false,
}
}
@@ -58,9 +62,11 @@ CurrencyInput.prototype.handleChange = function (newValue) {
if (value === '0' && newValue[newValueLastIndex] === '0') {
parsedValue = parsedValue.slice(0, newValueLastIndex)
}
-
const sanitizedValue = sanitizeValue(parsedValue)
- this.setState({ value: sanitizedValue })
+ this.setState({
+ value: sanitizedValue,
+ emptyState: newValue === '' && sanitizedValue === '0',
+ })
onInputChange(sanitizedValue)
}
@@ -85,18 +91,22 @@ CurrencyInput.prototype.render = function () {
placeholder,
readOnly,
inputRef,
+ type,
} = this.props
+ const { emptyState, focused } = this.state
const inputSizeMultiplier = readOnly ? 1 : 1.2
const valueToRender = this.getValueToRender()
-
return h('input', {
className,
- value: valueToRender,
- placeholder,
+ type,
+ value: emptyState ? '' : valueToRender,
+ placeholder: focused ? '' : placeholder,
size: valueToRender.length * inputSizeMultiplier,
readOnly,
+ onFocus: () => this.setState({ focused: true, emptyState: valueToRender === '0' }),
+ onBlur: () => this.setState({ focused: false, emptyState: false }),
onChange: e => this.handleChange(e.target.value),
ref: inputRef,
})
diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js
index 826d2cd4b..d8384c19d 100644
--- a/ui/app/components/customize-gas-modal/index.js
+++ b/ui/app/components/customize-gas-modal/index.js
@@ -3,6 +3,7 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
+const t = require('../../../i18n')
const GasModalCard = require('./gas-modal-card')
const ethUtil = require('ethereumjs-util')
@@ -21,12 +22,14 @@ const {
conversionUtil,
multiplyCurrencies,
conversionGreaterThan,
+ conversionMax,
subtractCurrencies,
} = require('../../conversion-util')
const {
getGasPrice,
getGasLimit,
+ getForceGasMin,
conversionRateSelector,
getSendAmount,
getSelectedToken,
@@ -44,6 +47,7 @@ function mapStateToProps (state) {
return {
gasPrice: getGasPrice(state),
gasLimit: getGasLimit(state),
+ forceGasMin: getForceGasMin(state),
conversionRate,
amount: getSendAmount(state),
maxModeOn: getSendMaxModeState(state),
@@ -114,9 +118,9 @@ CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
updateSendAmount(maxAmount)
}
- updateGasPrice(gasPrice)
- updateGasLimit(gasLimit)
- updateGasTotal(gasTotal)
+ updateGasPrice(ethUtil.addHexPrefix(gasPrice))
+ updateGasLimit(ethUtil.addHexPrefix(gasLimit))
+ updateGasTotal(ethUtil.addHexPrefix(gasTotal))
hideModal()
}
@@ -146,7 +150,7 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
})
if (!balanceIsSufficient) {
- error = 'Insufficient balance for current gas total'
+ error = t('balanceIsInsufficientGas')
}
const gasLimitTooLow = gasLimit && conversionGreaterThan(
@@ -162,7 +166,7 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
)
if (gasLimitTooLow) {
- error = 'Gas limit must be at least 21000'
+ error = t('gasLimitTooLow')
}
this.setState({ error })
@@ -217,7 +221,7 @@ CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
}
CustomizeGasModal.prototype.render = function () {
- const { hideModal } = this.props
+ const { hideModal, forceGasMin } = this.props
const { gasPrice, gasLimit, gasTotal, error, priceSigZeros, priceSigDec } = this.state
let convertedGasPrice = conversionUtil(gasPrice, {
@@ -229,6 +233,22 @@ CustomizeGasModal.prototype.render = function () {
convertedGasPrice += convertedGasPrice.match(/[.]/) ? priceSigZeros : `${priceSigDec}${priceSigZeros}`
+ let newGasPrice = gasPrice
+ if (forceGasMin) {
+ const convertedMinPrice = conversionUtil(forceGasMin, {
+ fromNumericBase: 'hex',
+ toNumericBase: 'dec',
+ })
+ convertedGasPrice = conversionMax(
+ { value: convertedMinPrice, fromNumericBase: 'dec' },
+ { value: convertedGasPrice, fromNumericBase: 'dec' }
+ )
+ newGasPrice = conversionMax(
+ { value: gasPrice, fromNumericBase: 'hex' },
+ { value: forceGasMin, fromNumericBase: 'hex' }
+ )
+ }
+
const convertedGasLimit = conversionUtil(gasLimit, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
@@ -239,7 +259,7 @@ CustomizeGasModal.prototype.render = function () {
}, [
h('div.send-v2__customize-gas__header', {}, [
- h('div.send-v2__customize-gas__title', 'Customize Gas'),
+ h('div.send-v2__customize-gas__title', t('customGas')),
h('div.send-v2__customize-gas__close', {
onClick: hideModal,
@@ -251,12 +271,12 @@ CustomizeGasModal.prototype.render = function () {
h(GasModalCard, {
value: convertedGasPrice,
- min: MIN_GAS_PRICE_GWEI,
+ min: forceGasMin || MIN_GAS_PRICE_GWEI,
// max: 1000,
step: multiplyCurrencies(MIN_GAS_PRICE_GWEI, 10),
onChange: value => this.convertAndSetGasPrice(value),
- title: 'Gas Price (GWEI)',
- copy: 'We calculate the suggested gas prices based on network success rates.',
+ title: t('gasPrice'),
+ copy: t('gasPriceCalculation'),
}),
h(GasModalCard, {
@@ -265,8 +285,8 @@ CustomizeGasModal.prototype.render = function () {
// max: 100000,
step: 1,
onChange: value => this.convertAndSetGasLimit(value),
- title: 'Gas Limit',
- copy: 'We calculate the suggested gas limit based on network success rates.',
+ title: t('gasLimit'),
+ copy: t('gasLimitCalculation'),
}),
]),
@@ -279,16 +299,16 @@ CustomizeGasModal.prototype.render = function () {
h('div.send-v2__customize-gas__revert', {
onClick: () => this.revert(),
- }, ['Revert']),
+ }, [t('revert')]),
h('div.send-v2__customize-gas__buttons', [
- h('div.send-v2__customize-gas__cancel', {
+ h('div.send-v2__customize-gas__cancel.allcaps', {
onClick: this.props.hideModal,
- }, ['CANCEL']),
+ }, [t('cancel')]),
- h(`div.send-v2__customize-gas__save${error ? '__error' : ''}`, {
- onClick: () => !error && this.save(gasPrice, gasLimit, gasTotal),
- }, ['SAVE']),
+ h(`div.send-v2__customize-gas__save${error ? '__error' : ''}.allcaps`, {
+ onClick: () => !error && this.save(newGasPrice, gasLimit, gasTotal),
+ }, [t('save')]),
]),
]),
diff --git a/ui/app/components/dropdowns/account-options-dropdown.js b/ui/app/components/dropdowns/account-options-dropdown.js
deleted file mode 100644
index f74c0a2d4..000000000
--- a/ui/app/components/dropdowns/account-options-dropdown.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const AccountDropdowns = require('./components/account-dropdowns')
-
-inherits(AccountOptionsDropdown, Component)
-function AccountOptionsDropdown () {
- Component.call(this)
-}
-
-module.exports = AccountOptionsDropdown
-
-// TODO: specify default props and proptypes
-// TODO: hook up to state, connect to redux to clean up API
-// TODO: selectedAddress is not defined... should we use selected?
-AccountOptionsDropdown.prototype.render = function () {
- const { selected, network, identities, style, dropdownWrapperStyle, menuItemStyles } = this.props
-
- return h(AccountDropdowns, {
- enableAccountOptions: true,
- enableAccountsSelector: false,
- selected,
- network,
- identities,
- style: style || {},
- dropdownWrapperStyle: dropdownWrapperStyle || {},
- menuItemStyles: menuItemStyles || {},
- }, [])
-}
diff --git a/ui/app/components/dropdowns/account-selection-dropdown.js b/ui/app/components/dropdowns/account-selection-dropdown.js
deleted file mode 100644
index 2f6452b15..000000000
--- a/ui/app/components/dropdowns/account-selection-dropdown.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const AccountDropdowns = require('./components/account-dropdowns')
-
-inherits(AccountSelectionDropdown, Component)
-function AccountSelectionDropdown () {
- Component.call(this)
-}
-
-module.exports = AccountSelectionDropdown
-
-// TODO: specify default props and proptypes
-// TODO: hook up to state, connect to redux to clean up API
-// TODO: selectedAddress is not defined... should we use selected?
-AccountSelectionDropdown.prototype.render = function () {
- const { selected, network, identities, style, dropdownWrapperStyle, menuItemStyles } = this.props
-
- return h(AccountDropdowns, {
- enableAccountOptions: false,
- enableAccountsSelector: true,
- selected,
- network,
- identities,
- style: style || {},
- dropdownWrapperStyle: dropdownWrapperStyle || {},
- menuItemStyles: menuItemStyles || {},
- }, [])
-}
diff --git a/ui/app/components/dropdowns/components/account-dropdowns.js b/ui/app/components/dropdowns/components/account-dropdowns.js
index 40918cc53..e5359c1d6 100644
--- a/ui/app/components/dropdowns/components/account-dropdowns.js
+++ b/ui/app/components/dropdowns/components/account-dropdowns.js
@@ -10,6 +10,7 @@ const Identicon = require('../../identicon')
const ethUtil = require('ethereumjs-util')
const copyToClipboard = require('copy-to-clipboard')
const { formatBalance } = require('../../../util')
+const t = require('../../../../i18n')
class AccountDropdowns extends Component {
constructor (props) {
@@ -121,7 +122,7 @@ class AccountDropdowns extends Component {
flex: '3 3 auto',
},
}, [
- h('span.account-dropdown-edit-button', {
+ h('span.account-dropdown-edit-button.allcaps', {
style: {
fontSize: '16px',
},
@@ -129,7 +130,7 @@ class AccountDropdowns extends Component {
actions.showEditAccountModal(identity)
},
}, [
- 'Edit',
+ t('edit'),
]),
]),
@@ -143,7 +144,7 @@ class AccountDropdowns extends Component {
try { // Sometimes keyrings aren't loaded yet:
const type = keyring.type
const isLoose = type !== 'HD Key Tree'
- return isLoose ? h('.keyring-label', 'IMPORTED') : null
+ return isLoose ? h('.keyring-label.allcaps', t('loose')) : null
} catch (e) { return }
}
@@ -201,7 +202,7 @@ class AccountDropdowns extends Component {
fontSize: '16px',
lineHeight: '23px',
},
- }, 'Create Account'),
+ }, t('createAccount')),
],
),
h(
@@ -235,7 +236,7 @@ class AccountDropdowns extends Component {
fontSize: '16px',
lineHeight: '23px',
},
- }, 'Import Account'),
+ }, t('importAccount')),
]
),
]
@@ -286,7 +287,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- 'Account Details',
+ t('accountDetails'),
),
h(
DropdownMenuItem,
@@ -302,7 +303,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- 'View account on Etherscan',
+ t('etherscanView'),
),
h(
DropdownMenuItem,
@@ -318,7 +319,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- 'Copy Address to clipboard',
+ t('copyAddress'),
),
h(
DropdownMenuItem,
@@ -330,7 +331,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- 'Export Private Key',
+ t('exportPrivateKey'),
),
h(
DropdownMenuItem,
@@ -345,7 +346,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- 'Add Token',
+ t('addToken'),
),
]
@@ -463,4 +464,3 @@ function mapStateToProps (state) {
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDropdowns)
-
diff --git a/ui/app/components/dropdowns/index.js b/ui/app/components/dropdowns/index.js
index fa66f5000..605507058 100644
--- a/ui/app/components/dropdowns/index.js
+++ b/ui/app/components/dropdowns/index.js
@@ -1,17 +1,11 @@
// Reusable Dropdown Components
// TODO: Refactor into separate components
const Dropdown = require('./components/dropdown').Dropdown
-const AccountDropdowns = require('./components/account-dropdowns')
// App-Specific Instances
-const AccountSelectionDropdown = require('./account-selection-dropdown')
-const AccountOptionsDropdown = require('./account-options-dropdown')
const NetworkDropdown = require('./network-dropdown').default
module.exports = {
- AccountSelectionDropdown,
- AccountOptionsDropdown,
NetworkDropdown,
Dropdown,
- AccountDropdowns,
}
diff --git a/ui/app/components/dropdowns/network-dropdown.js b/ui/app/components/dropdowns/network-dropdown.js
index ff10c0758..5afe730c1 100644
--- a/ui/app/components/dropdowns/network-dropdown.js
+++ b/ui/app/components/dropdowns/network-dropdown.js
@@ -6,6 +6,7 @@ const actions = require('../../actions')
const Dropdown = require('./components/dropdown').Dropdown
const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem
const NetworkDropdownIcon = require('./components/network-dropdown-icon')
+const t = require('../../../i18n')
const R = require('ramda')
// classes from nodes of the toggle element.
@@ -93,13 +94,13 @@ NetworkDropdown.prototype.render = function () {
}, [
h('div.network-dropdown-header', {}, [
- h('div.network-dropdown-title', {}, 'Networks'),
+ h('div.network-dropdown-title', {}, t('networks')),
h('div.network-dropdown-divider'),
h('div.network-dropdown-content',
{},
- 'The default network for Ether transactions is Main Net.'
+ t('defaultNetwork')
),
]),
@@ -121,7 +122,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: providerType === 'mainnet' ? '#ffffff' : '#9b9b9b',
},
- }, 'Main Ethereum Network'),
+ }, t('mainnet')),
]
),
@@ -143,7 +144,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: providerType === 'ropsten' ? '#ffffff' : '#9b9b9b',
},
- }, 'Ropsten Test Network'),
+ }, t('ropsten')),
]
),
@@ -165,7 +166,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: providerType === 'kovan' ? '#ffffff' : '#9b9b9b',
},
- }, 'Kovan Test Network'),
+ }, t('kovan')),
]
),
@@ -187,7 +188,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: providerType === 'rinkeby' ? '#ffffff' : '#9b9b9b',
},
- }, 'Rinkeby Test Network'),
+ }, t('rinkeby')),
]
),
@@ -209,7 +210,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: activeNetwork === 'http://localhost:8545' ? '#ffffff' : '#9b9b9b',
},
- }, 'Localhost 8545'),
+ }, t('localhost')),
]
),
@@ -233,7 +234,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: activeNetwork === 'custom' ? '#ffffff' : '#9b9b9b',
},
- }, 'Custom RPC'),
+ }, t('customRPC')),
]
),
@@ -248,15 +249,15 @@ NetworkDropdown.prototype.getNetworkName = function () {
let name
if (providerName === 'mainnet') {
- name = 'Main Ethereum Network'
+ name = t('mainnet')
} else if (providerName === 'ropsten') {
- name = 'Ropsten Test Network'
+ name = t('ropsten')
} else if (providerName === 'kovan') {
- name = 'Kovan Test Network'
+ name = t('kovan')
} else if (providerName === 'rinkeby') {
- name = 'Rinkeby Test Network'
+ name = t('rinkeby')
} else {
- name = 'Unknown Private Network'
+ name = t('unknownNetwork')
}
return name
diff --git a/ui/app/components/dropdowns/token-menu-dropdown.js b/ui/app/components/dropdowns/token-menu-dropdown.js
index dc7a985e3..a4f93b505 100644
--- a/ui/app/components/dropdowns/token-menu-dropdown.js
+++ b/ui/app/components/dropdowns/token-menu-dropdown.js
@@ -3,6 +3,7 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
+const t = require('../../../i18n')
module.exports = connect(null, mapDispatchToProps)(TokenMenuDropdown)
@@ -43,7 +44,7 @@ TokenMenuDropdown.prototype.render = function () {
showHideTokenConfirmationModal(this.props.token)
this.props.onClose()
},
- }, 'Hide Token'),
+ }, t('hideToken')),
]),
]),
diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js
index 6553053f7..add67ea35 100644
--- a/ui/app/components/ens-input.js
+++ b/ui/app/components/ens-input.js
@@ -8,6 +8,7 @@ const ENS = require('ethjs-ens')
const networkMap = require('ethjs-ens/lib/network-map.json')
const ensRE = /.+\..+$/
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
+const t = require('../../i18n')
module.exports = EnsInput
@@ -89,13 +90,13 @@ EnsInput.prototype.lookupEnsName = function () {
log.info(`ENS attempting to resolve name: ${recipient}`)
this.ens.lookup(recipient.trim())
.then((address) => {
- if (address === ZERO_ADDRESS) throw new Error('No address has been set for this name.')
+ if (address === ZERO_ADDRESS) throw new Error(t('noAddressForName'))
if (address !== ensResolution) {
this.setState({
loadingEns: false,
ensResolution: address,
nickname: recipient.trim(),
- hoverText: address + '\nClick to Copy',
+ hoverText: address + '\n' + t('clickCopy'),
ensFailure: false,
})
}
diff --git a/ui/app/components/hex-as-decimal-input.js b/ui/app/components/hex-as-decimal-input.js
index 4a71e9585..a43d44f89 100644
--- a/ui/app/components/hex-as-decimal-input.js
+++ b/ui/app/components/hex-as-decimal-input.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const extend = require('xtend')
+const t = require('../../i18n')
module.exports = HexAsDecimalInput
@@ -126,13 +127,13 @@ HexAsDecimalInput.prototype.constructWarning = function () {
let message = name ? name + ' ' : ''
if (min && max) {
- message += `must be greater than or equal to ${min} and less than or equal to ${max}.`
+ message += t('betweenMinAndMax', [min, max])
} else if (min) {
- message += `must be greater than or equal to ${min}.`
+ message += t('greaterThanMin', [min])
} else if (max) {
- message += `must be less than or equal to ${max}.`
+ message += t('lessThanMax', [max])
} else {
- message += 'Invalid input.'
+ message += t('invalidInput')
}
return message
diff --git a/ui/app/components/input-number.js b/ui/app/components/input-number.js
index fd8c5c309..5600e35ee 100644
--- a/ui/app/components/input-number.js
+++ b/ui/app/components/input-number.js
@@ -55,6 +55,7 @@ InputNumber.prototype.render = function () {
className: 'customize-gas-input',
value,
placeholder,
+ type: 'number',
onInputChange: newValue => {
this.setValue(newValue)
},
diff --git a/ui/app/components/mini-account-panel.js b/ui/app/components/mini-account-panel.js
deleted file mode 100644
index c09cf5b7a..000000000
--- a/ui/app/components/mini-account-panel.js
+++ /dev/null
@@ -1,74 +0,0 @@
-const inherits = require('util').inherits
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const Identicon = require('./identicon')
-
-module.exports = AccountPanel
-
-
-inherits(AccountPanel, Component)
-function AccountPanel () {
- Component.call(this)
-}
-
-AccountPanel.prototype.render = function () {
- var props = this.props
- var picOrder = props.picOrder || 'left'
- const { imageSeed } = props
-
- return (
-
- h('.identity-panel.flex-row.flex-left', {
- style: {
- cursor: props.onClick ? 'pointer' : undefined,
- },
- onClick: props.onClick,
- }, [
-
- this.genIcon(imageSeed, picOrder),
-
- h('div.flex-column.flex-justify-center', {
- style: {
- lineHeight: '15px',
- order: 2,
- display: 'flex',
- alignItems: picOrder === 'left' ? 'flex-begin' : 'flex-end',
- },
- }, this.props.children),
- ])
- )
-}
-
-AccountPanel.prototype.genIcon = function (seed, picOrder) {
- const props = this.props
-
- // When there is no seed value, this is a contract creation.
- // We then show the contract icon.
- if (!seed) {
- return h('.identicon-wrapper.flex-column.select-none', {
- style: {
- order: picOrder === 'left' ? 1 : 3,
- },
- }, [
- h('i.fa.fa-file-text-o.fa-lg', {
- style: {
- fontSize: '42px',
- transform: 'translate(0px, -16px)',
- },
- }),
- ])
- }
-
- // If there was a seed, we return an identicon for that address.
- return h('.identicon-wrapper.flex-column.select-none', {
- style: {
- order: picOrder === 'left' ? 1 : 3,
- },
- }, [
- h(Identicon, {
- address: seed,
- imageify: props.imageifyIdenticons,
- }),
- ])
-}
-
diff --git a/ui/app/components/modals/account-details-modal.js b/ui/app/components/modals/account-details-modal.js
index c1f7a3236..75f989e86 100644
--- a/ui/app/components/modals/account-details-modal.js
+++ b/ui/app/components/modals/account-details-modal.js
@@ -8,6 +8,7 @@ const { getSelectedIdentity } = require('../../selectors')
const genAccountLink = require('../../../lib/account-link.js')
const QrView = require('../qr-code')
const EditableLabel = require('../editable-label')
+const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -64,12 +65,12 @@ AccountDetailsModal.prototype.render = function () {
h('button.btn-clear.account-modal__button', {
onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }),
- }, 'View account on Etherscan'),
+ }, t('etherscanView')),
// Holding on redesign for Export Private Key functionality
h('button.btn-clear.account-modal__button', {
onClick: () => showExportPrivateKeyModal(),
- }, 'Export private key'),
+ }, t('exportPrivateKey')),
])
}
diff --git a/ui/app/components/modals/account-modal-container.js b/ui/app/components/modals/account-modal-container.js
index c548fb7b3..08540aa76 100644
--- a/ui/app/components/modals/account-modal-container.js
+++ b/ui/app/components/modals/account-modal-container.js
@@ -5,6 +5,7 @@ const connect = require('react-redux').connect
const actions = require('../../actions')
const { getSelectedIdentity } = require('../../selectors')
const Identicon = require('../identicon')
+const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -59,7 +60,7 @@ AccountModalContainer.prototype.render = function () {
h('i.fa.fa-angle-left.fa-lg'),
- h('span.account-modal-back__text', ' Back'),
+ h('span.account-modal-back__text', ' ' + t('back')),
]),
diff --git a/ui/app/components/modals/buy-options-modal.js b/ui/app/components/modals/buy-options-modal.js
index 74a7a847e..7eb73c3a6 100644
--- a/ui/app/components/modals/buy-options-modal.js
+++ b/ui/app/components/modals/buy-options-modal.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
const networkNames = require('../../../../app/scripts/config.js').networkNames
+const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -56,15 +57,15 @@ BuyOptions.prototype.render = function () {
}, [
h('div.buy-modal-content-title', {
style: {},
- }, 'Transfers'),
- h('div', {}, 'How would you like to deposit Ether?'),
+ }, t('transfers')),
+ h('div', {}, t('howToDeposit')),
]),
h('div.buy-modal-content-options.flex-column.flex-center', {}, [
isTestNetwork
- ? this.renderModalContentOption(networkName, 'Test Faucet', () => toFaucet(network))
- : this.renderModalContentOption('Coinbase', 'Deposit with Fiat', () => toCoinbase(address)),
+ ? this.renderModalContentOption(networkName, t('testFaucet'), () => toFaucet(network))
+ : this.renderModalContentOption('Coinbase', t('depositFiat'), () => toCoinbase(address)),
// h('div.buy-modal-content-option', {}, [
// h('div.buy-modal-content-option-title', {}, 'Shapeshift'),
@@ -72,8 +73,8 @@ BuyOptions.prototype.render = function () {
// ]),,
this.renderModalContentOption(
- 'Direct Deposit',
- 'Deposit from another account',
+ t('directDeposit'),
+ t('depositFromAccount'),
() => this.goToAccountDetailsModal()
),
@@ -84,7 +85,7 @@ BuyOptions.prototype.render = function () {
background: 'white',
},
onClick: () => { this.props.hideModal() },
- }, h('div.buy-modal-content-footer#buy-modal-content-footer-text', {}, 'Cancel')),
+ }, h('div.buy-modal-content-footer#buy-modal-content-footer-text', {}, t('cancel'))),
]),
])
}
diff --git a/ui/app/components/modals/deposit-ether-modal.js b/ui/app/components/modals/deposit-ether-modal.js
index 7547dbcf5..b642513d7 100644
--- a/ui/app/components/modals/deposit-ether-modal.js
+++ b/ui/app/components/modals/deposit-ether-modal.js
@@ -5,18 +5,18 @@ const connect = require('react-redux').connect
const actions = require('../../actions')
const networkNames = require('../../../../app/scripts/config.js').networkNames
const ShapeshiftForm = require('../shapeshift-form')
-
-const DIRECT_DEPOSIT_ROW_TITLE = 'Directly Deposit Ether'
-const DIRECT_DEPOSIT_ROW_TEXT = `If you already have some Ether, the quickest way to get Ether in
-your new wallet by direct deposit.`
-const COINBASE_ROW_TITLE = 'Buy on Coinbase'
-const COINBASE_ROW_TEXT = `Coinbase is the world’s most popular way to buy and sell bitcoin,
-ethereum, and litecoin.`
-const SHAPESHIFT_ROW_TITLE = 'Deposit with ShapeShift'
-const SHAPESHIFT_ROW_TEXT = `If you own other cryptocurrencies, you can trade and deposit Ether
-directly into your MetaMask wallet. No Account Needed.`
-const FAUCET_ROW_TITLE = 'Test Faucet'
-const facuetRowText = networkName => `Get Ether from a faucet for the ${networkName}`
+const t = require('../../../i18n')
+
+const DIRECT_DEPOSIT_ROW_TITLE = t('directDepositEther')
+const DIRECT_DEPOSIT_ROW_TEXT = t('directDepositEtherExplainer')
+const COINBASE_ROW_TITLE = t('buyCoinbase')
+const COINBASE_ROW_TEXT = t('buyCoinbaseExplainer')
+const SHAPESHIFT_ROW_TITLE = t('depositShapeShift')
+const SHAPESHIFT_ROW_TEXT = t('depositShapeShiftExplainer')
+const FAUCET_ROW_TITLE = t('testFaucet')
+const facuetRowText = (networkName) => {
+ return t('getEtherFromFaucet', [networkName])
+}
function mapStateToProps (state) {
return {
@@ -83,7 +83,7 @@ DepositEtherModal.prototype.renderRow = function ({
]),
- h('div.deposit-ether-modal__buy-row__logo', [logo]),
+ h('div.deposit-ether-modal__buy-row__logo-container', [logo]),
h('div.deposit-ether-modal__buy-row__description', [
@@ -109,17 +109,17 @@ DepositEtherModal.prototype.render = function () {
const isTestNetwork = ['3', '4', '42'].find(n => n === network)
const networkName = networkNames[network]
- return h('div.deposit-ether-modal', {}, [
+ return h('div.page-container.page-container--full-width.page-container--full-height', {}, [
- h('div.deposit-ether-modal__header', [
+ h('div.page-container__header', [
- h('div.deposit-ether-modal__header__title', ['Deposit Ether']),
+ h('div.page-container__title', [t('depositEther')]),
- h('div.deposit-ether-modal__header__description', [
- 'To interact with decentralized applications using MetaMask, you’ll need Ether in your wallet.',
+ h('div.page-container__subtitle', [
+ t('needEtherInWallet'),
]),
- h('div.deposit-ether-modal__header__close', {
+ h('div.page-container__header-close', {
onClick: () => {
this.setState({ buyingWithShapeshift: false })
this.props.hideWarning()
@@ -129,54 +129,65 @@ DepositEtherModal.prototype.render = function () {
]),
- h('div.deposit-ether-modal__buy-rows', [
+ h('.page-container__content', {}, [
- this.renderRow({
- logo: h('img.deposit-ether-modal__buy-row__eth-logo', { src: '../../../images/eth_logo.svg' }),
- title: DIRECT_DEPOSIT_ROW_TITLE,
- text: DIRECT_DEPOSIT_ROW_TEXT,
- buttonLabel: 'View Account',
- onButtonClick: () => this.goToAccountDetailsModal(),
- hide: buyingWithShapeshift,
- }),
+ h('div.deposit-ether-modal__buy-rows', [
- this.renderRow({
- logo: h('i.fa.fa-tint.fa-2x'),
- title: FAUCET_ROW_TITLE,
- text: facuetRowText(networkName),
- buttonLabel: 'Get Ether',
- onButtonClick: () => toFaucet(network),
- hide: !isTestNetwork || buyingWithShapeshift,
- }),
+ this.renderRow({
+ logo: h('img.deposit-ether-modal__logo', {
+ src: '../../../images/deposit-eth.svg',
+ }),
+ title: DIRECT_DEPOSIT_ROW_TITLE,
+ text: DIRECT_DEPOSIT_ROW_TEXT,
+ buttonLabel: t('viewAccount'),
+ onButtonClick: () => this.goToAccountDetailsModal(),
+ hide: buyingWithShapeshift,
+ }),
- this.renderRow({
- logo: h('img.deposit-ether-modal__buy-row__coinbase-logo', {
- src: '../../../images/coinbase logo.png',
+ this.renderRow({
+ logo: h('i.fa.fa-tint.fa-2x'),
+ title: FAUCET_ROW_TITLE,
+ text: facuetRowText(networkName),
+ buttonLabel: t('getEther'),
+ onButtonClick: () => toFaucet(network),
+ hide: !isTestNetwork || buyingWithShapeshift,
}),
- title: COINBASE_ROW_TITLE,
- text: COINBASE_ROW_TEXT,
- buttonLabel: 'Continue to Coinbase',
- onButtonClick: () => toCoinbase(address),
- hide: isTestNetwork || buyingWithShapeshift,
- }),
- this.renderRow({
- logo: h('img.deposit-ether-modal__buy-row__shapeshift-logo', {
- src: '../../../images/shapeshift logo.png',
+ this.renderRow({
+ logo: h('div.deposit-ether-modal__logo', {
+ style: {
+ backgroundImage: 'url(\'../../../images/coinbase logo.png\')',
+ height: '40px',
+ },
+ }),
+ title: COINBASE_ROW_TITLE,
+ text: COINBASE_ROW_TEXT,
+ buttonLabel: t('continueToCoinbase'),
+ onButtonClick: () => toCoinbase(address),
+ hide: isTestNetwork || buyingWithShapeshift,
+ }),
+
+ this.renderRow({
+ logo: h('div.deposit-ether-modal__logo', {
+ style: {
+ backgroundImage: 'url(\'../../../images/shapeshift logo.png\')',
+ },
+ }),
+ title: SHAPESHIFT_ROW_TITLE,
+ text: SHAPESHIFT_ROW_TEXT,
+ buttonLabel: t('shapeshiftBuy'),
+ onButtonClick: () => this.setState({ buyingWithShapeshift: true }),
+ hide: isTestNetwork,
+ hideButton: buyingWithShapeshift,
+ hideTitle: buyingWithShapeshift,
+ onBackClick: () => this.setState({ buyingWithShapeshift: false }),
+ showBackButton: this.state.buyingWithShapeshift,
+ className: buyingWithShapeshift && 'deposit-ether-modal__buy-row__shapeshift-buy',
}),
- title: SHAPESHIFT_ROW_TITLE,
- text: SHAPESHIFT_ROW_TEXT,
- buttonLabel: 'Buy with Shapeshift',
- onButtonClick: () => this.setState({ buyingWithShapeshift: true }),
- hide: isTestNetwork,
- hideButton: buyingWithShapeshift,
- hideTitle: buyingWithShapeshift,
- onBackClick: () => this.setState({ buyingWithShapeshift: false }),
- showBackButton: this.state.buyingWithShapeshift,
- className: buyingWithShapeshift && 'deposit-ether-modal__buy-row__shapeshift-buy',
- }),
- buyingWithShapeshift && h(ShapeshiftForm),
+ buyingWithShapeshift && h(ShapeshiftForm),
+
+ ]),
]),
])
diff --git a/ui/app/components/modals/edit-account-name-modal.js b/ui/app/components/modals/edit-account-name-modal.js
index e2361140d..6efa8d476 100644
--- a/ui/app/components/modals/edit-account-name-modal.js
+++ b/ui/app/components/modals/edit-account-name-modal.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
const { getSelectedAccount } = require('../../selectors')
+const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -50,7 +51,7 @@ EditAccountNameModal.prototype.render = function () {
]),
h('div.edit-account-name-modal-title', {
- }, ['Edit Account Name']),
+ }, [t('editAccountName')]),
h('input.edit-account-name-modal-input', {
placeholder: identity.name,
@@ -60,7 +61,7 @@ EditAccountNameModal.prototype.render = function () {
value: this.state.inputText,
}, []),
- h('button.btn-clear.edit-account-name-modal-save-button', {
+ h('button.btn-clear.edit-account-name-modal-save-button.allcaps', {
onClick: () => {
if (this.state.inputText.length !== 0) {
saveAccountLabel(identity.address, this.state.inputText)
@@ -69,7 +70,7 @@ EditAccountNameModal.prototype.render = function () {
},
disabled: this.state.inputText.length === 0,
}, [
- 'SAVE',
+ t('save'),
]),
]),
diff --git a/ui/app/components/modals/export-private-key-modal.js b/ui/app/components/modals/export-private-key-modal.js
index 422f23f44..017177cfd 100644
--- a/ui/app/components/modals/export-private-key-modal.js
+++ b/ui/app/components/modals/export-private-key-modal.js
@@ -7,6 +7,7 @@ const actions = require('../../actions')
const AccountModalContainer = require('./account-modal-container')
const { getSelectedIdentity } = require('../../selectors')
const ReadOnlyInput = require('../readonly-input')
+const t = require('../../../i18n')
const copyToClipboard = require('copy-to-clipboard')
function mapStateToProps (state) {
@@ -48,8 +49,8 @@ ExportPrivateKeyModal.prototype.exportAccountAndGetPrivateKey = function (passwo
ExportPrivateKeyModal.prototype.renderPasswordLabel = function (privateKey) {
return h('span.private-key-password-label', privateKey
- ? 'This is your private key (click to copy)'
- : 'Type Your Password'
+ ? t('copyPrivateKey')
+ : t('typePassword')
)
}
@@ -86,8 +87,8 @@ ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password,
),
(privateKey
- ? this.renderButton('btn-clear export-private-key__button', () => hideModal(), 'Done')
- : this.renderButton('btn-clear export-private-key__button', () => this.exportAccountAndGetPrivateKey(this.state.password, address), 'Confirm')
+ ? this.renderButton('btn-clear export-private-key__button', () => hideModal(), t('done'))
+ : this.renderButton('btn-clear export-private-key__button', () => this.exportAccountAndGetPrivateKey(this.state.password, address), t('confirm'))
),
])
@@ -120,7 +121,7 @@ ExportPrivateKeyModal.prototype.render = function () {
h('div.account-modal-divider'),
- h('span.modal-body-title', 'Show Private Keys'),
+ h('span.modal-body-title', t('showPrivateKeys')),
h('div.private-key-password', {}, [
this.renderPasswordLabel(privateKey),
@@ -130,10 +131,7 @@ ExportPrivateKeyModal.prototype.render = function () {
!warning ? null : h('span.private-key-password-error', warning),
]),
- h('div.private-key-password-warning', `Warning: Never disclose this key.
- Anyone with your private keys can take steal any assets held in your
- account.`
- ),
+ h('div.private-key-password-warning', t('privateKeyWarning')),
this.renderButtons(privateKey, this.state.password, address, hideModal),
diff --git a/ui/app/components/modals/hide-token-confirmation-modal.js b/ui/app/components/modals/hide-token-confirmation-modal.js
index 56c7ba299..33d8062c6 100644
--- a/ui/app/components/modals/hide-token-confirmation-modal.js
+++ b/ui/app/components/modals/hide-token-confirmation-modal.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
const Identicon = require('../identicon')
+const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -41,7 +42,7 @@ HideTokenConfirmationModal.prototype.render = function () {
h('div.hide-token-confirmation__container', {
}, [
h('div.hide-token-confirmation__title', {}, [
- 'Hide Token?',
+ t('hideTokenPrompt'),
]),
h(Identicon, {
@@ -54,19 +55,19 @@ HideTokenConfirmationModal.prototype.render = function () {
h('div.hide-token-confirmation__symbol', {}, symbol),
h('div.hide-token-confirmation__copy', {}, [
- 'You can add this token back in the future by going go to “Add token” in your accounts options menu.',
+ t('readdToken'),
]),
h('div.hide-token-confirmation__buttons', {}, [
- h('button.btn-cancel.hide-token-confirmation__button', {
+ h('button.btn-cancel.hide-token-confirmation__button.allcaps', {
onClick: () => hideModal(),
}, [
- 'CANCEL',
+ t('cancel'),
]),
- h('button.btn-clear.hide-token-confirmation__button', {
+ h('button.btn-clear.hide-token-confirmation__button.allcaps', {
onClick: () => hideToken(address),
}, [
- 'HIDE',
+ t('hide'),
]),
]),
]),
diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js
index 8e9e58985..501b83430 100644
--- a/ui/app/components/modals/modal.js
+++ b/ui/app/components/modals/modal.js
@@ -6,6 +6,7 @@ const FadeModal = require('boron').FadeModal
const actions = require('../../actions')
const isMobileView = require('../../../lib/is-mobile-view')
const isPopupOrNotification = require('../../../../app/scripts/lib/is-popup-or-notification')
+const t = require('../../../i18n')
// Modal Components
const BuyOptions = require('./buy-options-modal')
@@ -92,18 +93,20 @@ const MODALS = {
display: 'flex',
},
laptopModalStyle: {
- width: '900px',
- maxWidth: '900px',
+ width: '850px',
top: 'calc(10% + 10px)',
left: '0',
right: '0',
margin: '0 auto',
boxShadow: '0 0 6px 0 rgba(0,0,0,0.3)',
- borderRadius: '8px',
+ borderRadius: '7px',
transform: 'none',
+ height: 'calc(80% - 20px)',
+ overflowY: 'hidden',
},
contentStyle: {
- borderRadius: '8px',
+ borderRadius: '7px',
+ height: '100%',
},
},
@@ -171,9 +174,8 @@ const MODALS = {
BETA_UI_NOTIFICATION_MODAL: {
contents: [
h(NotifcationModal, {
- header: 'Welcome to the New UI (Beta)',
- message: `You are now using the new Metamask UI. Take a look around, try out new features like sending tokens,
- and let us know if you have any issues.`,
+ header: t('uiWelcome'),
+ message: t('uiWelcomeMessage'),
}),
],
mobileModalStyle: {
@@ -189,9 +191,8 @@ const MODALS = {
OLD_UI_NOTIFICATION_MODAL: {
contents: [
h(NotifcationModal, {
- header: 'Old UI',
- message: `You have returned to the old UI. You can switch back to the New UI through the option in the top
- right dropdown menu.`,
+ header: t('oldUI'),
+ message: t('oldUIMessage'),
}),
],
mobileModalStyle: {
diff --git a/ui/app/components/modals/new-account-modal.js b/ui/app/components/modals/new-account-modal.js
index fc1fd413d..298b76af4 100644
--- a/ui/app/components/modals/new-account-modal.js
+++ b/ui/app/components/modals/new-account-modal.js
@@ -3,6 +3,7 @@ const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const { connect } = require('react-redux')
const actions = require('../../actions')
+const t = require('../../../i18n')
class NewAccountModal extends Component {
constructor (props) {
@@ -11,7 +12,7 @@ class NewAccountModal extends Component {
const newAccountNumber = numberOfExistingAccounts + 1
this.state = {
- newAccountName: `Account ${newAccountNumber}`,
+ newAccountName: `${t('account')} ${newAccountNumber}`,
}
}
@@ -22,7 +23,7 @@ class NewAccountModal extends Component {
h('div.new-account-modal-wrapper', {
}, [
h('div.new-account-modal-header', {}, [
- 'New Account',
+ t('newAccount'),
]),
h('div.modal-close-x', {
@@ -30,19 +31,19 @@ class NewAccountModal extends Component {
}),
h('div.new-account-modal-content', {}, [
- 'Account Name',
+ t('accountName'),
]),
h('div.new-account-input-wrapper', {}, [
h('input.new-account-input', {
value: this.state.newAccountName,
- placeholder: 'E.g. My new account',
+ placeholder: t('sampleAccountName'),
onChange: event => this.setState({ newAccountName: event.target.value }),
}, []),
]),
h('div.new-account-modal-content.after-input', {}, [
- 'or',
+ t('or'),
]),
h('div.new-account-modal-content.after-input.pointer', {
@@ -50,13 +51,13 @@ class NewAccountModal extends Component {
this.props.hideModal()
this.props.showImportPage()
},
- }, 'Import an account'),
+ }, t('importAnAccount')),
- h('div.new-account-modal-content.button', {}, [
+ h('div.new-account-modal-content.button.allcaps', {}, [
h('button.btn-clear', {
onClick: () => this.props.createAccount(newAccountName),
}, [
- 'SAVE',
+ t('save'),
]),
]),
]),
diff --git a/ui/app/components/network.js b/ui/app/components/network.js
index 3e91fa807..f3df2242a 100644
--- a/ui/app/components/network.js
+++ b/ui/app/components/network.js
@@ -3,6 +3,7 @@ const h = require('react-hyperscript')
const classnames = require('classnames')
const inherits = require('util').inherits
const NetworkDropdownIcon = require('./dropdowns/components/network-dropdown-icon')
+const t = require('../../i18n')
module.exports = Network
@@ -33,7 +34,7 @@ Network.prototype.render = function () {
onClick: (event) => this.props.onClick(event),
}, [
h('img', {
- title: 'Attempting to connect to blockchain.',
+ title: t('attemptingConnect'),
style: {
width: '27px',
},
@@ -41,22 +42,22 @@ Network.prototype.render = function () {
}),
])
} else if (providerName === 'mainnet') {
- hoverText = 'Main Ethereum Network'
+ hoverText = t('mainnet')
iconName = 'ethereum-network'
} else if (providerName === 'ropsten') {
- hoverText = 'Ropsten Test Network'
+ hoverText = t('ropsten')
iconName = 'ropsten-test-network'
} else if (parseInt(networkNumber) === 3) {
- hoverText = 'Ropsten Test Network'
+ hoverText = t('ropsten')
iconName = 'ropsten-test-network'
} else if (providerName === 'kovan') {
- hoverText = 'Kovan Test Network'
+ hoverText = t('kovan')
iconName = 'kovan-test-network'
} else if (providerName === 'rinkeby') {
- hoverText = 'Rinkeby Test Network'
+ hoverText = t('rinkeby')
iconName = 'rinkeby-test-network'
} else {
- hoverText = 'Unknown Private Network'
+ hoverText = t('unknownNetwork')
iconName = 'unknown-private-network'
}
@@ -84,7 +85,7 @@ Network.prototype.render = function () {
backgroundColor: '#038789', // $blue-lagoon
nonSelectBackgroundColor: '#15afb2',
}),
- h('.network-name', 'Main Network'),
+ h('.network-name', t('mainnet')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
case 'ropsten-test-network':
@@ -93,7 +94,7 @@ Network.prototype.render = function () {
backgroundColor: '#e91550', // $crimson
nonSelectBackgroundColor: '#ec2c50',
}),
- h('.network-name', 'Ropsten Test Net'),
+ h('.network-name', t('ropsten')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
case 'kovan-test-network':
@@ -102,7 +103,7 @@ Network.prototype.render = function () {
backgroundColor: '#690496', // $purple
nonSelectBackgroundColor: '#b039f3',
}),
- h('.network-name', 'Kovan Test Net'),
+ h('.network-name', t('kovan')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
case 'rinkeby-test-network':
@@ -111,7 +112,7 @@ Network.prototype.render = function () {
backgroundColor: '#ebb33f', // $tulip-tree
nonSelectBackgroundColor: '#ecb23e',
}),
- h('.network-name', 'Rinkeby Test Net'),
+ h('.network-name', t('rinkeby')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
default:
@@ -123,7 +124,7 @@ Network.prototype.render = function () {
},
}),
- h('.network-name', 'Private Network'),
+ h('.network-name', t('privateNetwork')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
}
diff --git a/ui/app/components/notice.js b/ui/app/components/notice.js
index 9d2e89cb0..8b0ce1e8b 100644
--- a/ui/app/components/notice.js
+++ b/ui/app/components/notice.js
@@ -4,6 +4,7 @@ const h = require('react-hyperscript')
const ReactMarkdown = require('react-markdown')
const linker = require('extension-link-enabler')
const findDOMNode = require('react-dom').findDOMNode
+const t = require('../../i18n')
module.exports = Notice
@@ -110,7 +111,7 @@ Notice.prototype.render = function () {
style: {
marginTop: '18px',
},
- }, 'Accept'),
+ }, t('accept')),
])
)
}
diff --git a/ui/app/components/pending-msg-details.js b/ui/app/components/pending-msg-details.js
index 718a22de0..87e66855d 100644
--- a/ui/app/components/pending-msg-details.js
+++ b/ui/app/components/pending-msg-details.js
@@ -1,6 +1,7 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
+const t = require('../../i18n')
const AccountPanel = require('./account-panel')
@@ -39,7 +40,7 @@ PendingMsgDetails.prototype.render = function () {
// message data
h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [
h('.flex-column.flex-space-between', [
- h('label.font-small', 'MESSAGE'),
+ h('label.font-small.allcaps', t('message')),
h('span.font-small', msgParams.data),
]),
]),
@@ -47,4 +48,3 @@ PendingMsgDetails.prototype.render = function () {
])
)
}
-
diff --git a/ui/app/components/pending-msg.js b/ui/app/components/pending-msg.js
index 834719c53..dc406b955 100644
--- a/ui/app/components/pending-msg.js
+++ b/ui/app/components/pending-msg.js
@@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const PendingTxDetails = require('./pending-msg-details')
+const t = require('../../i18n')
module.exports = PendingMsg
@@ -29,17 +30,14 @@ PendingMsg.prototype.render = function () {
fontWeight: 'bold',
textAlign: 'center',
},
- }, 'Sign Message'),
+ }, t('signMessage')),
h('.error', {
style: {
margin: '10px',
},
}, [
- `Signing this message can have
- dangerous side effects. Only sign messages from
- sites you fully trust with your entire account.
- This dangerous method will be removed in a future version. `,
+ t('signNotice'),
h('a', {
href: 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527',
style: { color: 'rgb(247, 134, 28)' },
@@ -48,7 +46,7 @@ PendingMsg.prototype.render = function () {
const url = 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527'
global.platform.openWindow({ url })
},
- }, 'Read more here.'),
+ }, t('readMore')),
]),
// message details
@@ -58,13 +56,12 @@ PendingMsg.prototype.render = function () {
h('.flex-row.flex-space-around', [
h('button', {
onClick: state.cancelMessage,
- }, 'Cancel'),
+ }, t('cancel')),
h('button', {
onClick: state.signMessage,
- }, 'Sign'),
+ }, t('sign')),
]),
])
)
}
-
diff --git a/ui/app/components/pending-personal-msg-details.js b/ui/app/components/pending-personal-msg-details.js
deleted file mode 100644
index 1050513f2..000000000
--- a/ui/app/components/pending-personal-msg-details.js
+++ /dev/null
@@ -1,60 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-const AccountPanel = require('./account-panel')
-const BinaryRenderer = require('./binary-renderer')
-
-module.exports = PendingMsgDetails
-
-inherits(PendingMsgDetails, Component)
-function PendingMsgDetails () {
- Component.call(this)
-}
-
-PendingMsgDetails.prototype.render = function () {
- var state = this.props
- var msgData = state.txData
-
- var msgParams = msgData.msgParams || {}
- var address = msgParams.from || state.selectedAddress
- var identity = state.identities[address] || { address: address }
- var account = state.accounts[address] || { address: address }
-
- var { data } = msgParams
-
- return (
- h('div', {
- key: msgData.id,
- style: {
- margin: '10px 20px',
- },
- }, [
-
- // account that will sign
- h(AccountPanel, {
- showFullAddress: true,
- identity: identity,
- account: account,
- imageifyIdenticons: state.imageifyIdenticons,
- }),
-
- // message data
- h('div', {
- style: {
- height: '260px',
- },
- }, [
- h('label.font-small', { style: { display: 'block' } }, 'MESSAGE'),
- h(BinaryRenderer, {
- value: data,
- style: {
- height: '215px',
- },
- }),
- ]),
-
- ])
- )
-}
-
diff --git a/ui/app/components/pending-tx/confirm-deploy-contract.js b/ui/app/components/pending-tx/confirm-deploy-contract.js
index ae6c6ef7b..4810bb515 100644
--- a/ui/app/components/pending-tx/confirm-deploy-contract.js
+++ b/ui/app/components/pending-tx/confirm-deploy-contract.js
@@ -1,348 +1,350 @@
-const Component = require('react').Component
+const { Component } = require('react')
const { connect } = require('react-redux')
const h = require('react-hyperscript')
-const inherits = require('util').inherits
+const PropTypes = require('prop-types')
const actions = require('../../actions')
const clone = require('clone')
-const Identicon = require('../identicon')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
const { conversionUtil } = require('../../conversion-util')
+const t = require('../../../i18n')
+const SenderToRecipient = require('../sender-to-recipient')
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
+class ConfirmDeployContract extends Component {
+ constructor (props) {
+ super(props)
-module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmDeployContract)
-
-function mapStateToProps (state) {
- const {
- conversionRate,
- identities,
- currentCurrency,
- } = state.metamask
- const accounts = state.metamask.accounts
- const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
- return {
- currentCurrency,
- conversionRate,
- identities,
- selectedAddress,
+ this.state = {
+ valid: false,
+ submitting: false,
+ }
}
-}
-function mapDispatchToProps (dispatch) {
- return {
- backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)),
- cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
+ onSubmit (event) {
+ event.preventDefault()
+ const txMeta = this.gatherTxMeta()
+ const valid = this.checkValidity()
+ this.setState({ valid, submitting: true })
+
+ if (valid && this.verifyGasParams()) {
+ this.props.sendTransaction(txMeta, event)
+ } else {
+ this.props.displayWarning('invalidGasParams')
+ this.setState({ submitting: false })
+ }
}
-}
-
-inherits(ConfirmDeployContract, Component)
-function ConfirmDeployContract () {
- Component.call(this)
- this.state = {}
- this.onSubmit = this.onSubmit.bind(this)
-}
+ cancel (event, txMeta) {
+ event.preventDefault()
+ this.props.cancelTransaction(txMeta)
+ }
-ConfirmDeployContract.prototype.onSubmit = function (event) {
- event.preventDefault()
- const txMeta = this.gatherTxMeta()
- const valid = this.checkValidity()
- this.setState({ valid, submitting: true })
-
- if (valid && this.verifyGasParams()) {
- this.props.sendTransaction(txMeta, event)
- } else {
- this.props.dispatch(actions.displayWarning('Invalid Gas Parameters'))
- this.setState({ submitting: false })
+ checkValidity () {
+ const form = this.getFormEl()
+ const valid = form.checkValidity()
+ return valid
}
-}
-ConfirmDeployContract.prototype.cancel = function (event, txMeta) {
- event.preventDefault()
- this.props.cancelTransaction(txMeta)
-}
+ getFormEl () {
+ const form = document.querySelector('form#pending-tx-form')
+ // Stub out form for unit tests:
+ if (!form) {
+ return { checkValidity () { return true } }
+ }
+ return form
+ }
-ConfirmDeployContract.prototype.checkValidity = function () {
- const form = this.getFormEl()
- const valid = form.checkValidity()
- return valid
-}
+ // After a customizable state value has been updated,
+ gatherTxMeta () {
+ const props = this.props
+ const state = this.state
+ const txData = clone(state.txData) || clone(props.txData)
-ConfirmDeployContract.prototype.getFormEl = function () {
- const form = document.querySelector('form#pending-tx-form')
- // Stub out form for unit tests:
- if (!form) {
- return { checkValidity () { return true } }
+ // log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
+ return txData
}
- return form
-}
-
-// After a customizable state value has been updated,
-ConfirmDeployContract.prototype.gatherTxMeta = function () {
- const props = this.props
- const state = this.state
- const txData = clone(state.txData) || clone(props.txData)
- // log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
- return txData
-}
+ verifyGasParams () {
+ // We call this in case the gas has not been modified at all
+ if (!this.state) { return true }
+ return (
+ this._notZeroOrEmptyString(this.state.gas) &&
+ this._notZeroOrEmptyString(this.state.gasPrice)
+ )
+ }
-ConfirmDeployContract.prototype.verifyGasParams = function () {
- // We call this in case the gas has not been modified at all
- if (!this.state) { return true }
- return (
- this._notZeroOrEmptyString(this.state.gas) &&
- this._notZeroOrEmptyString(this.state.gasPrice)
- )
-}
+ _notZeroOrEmptyString (obj) {
+ return obj !== '' && obj !== '0x0'
+ }
-ConfirmDeployContract.prototype._notZeroOrEmptyString = function (obj) {
- return obj !== '' && obj !== '0x0'
-}
+ bnMultiplyByFraction (targetBN, numerator, denominator) {
+ const numBN = new BN(numerator)
+ const denomBN = new BN(denominator)
+ return targetBN.mul(numBN).div(denomBN)
+ }
-ConfirmDeployContract.prototype.bnMultiplyByFraction = function (targetBN, numerator, denominator) {
- const numBN = new BN(numerator)
- const denomBN = new BN(denominator)
- return targetBN.mul(numBN).div(denomBN)
-}
+ getData () {
+ const { identities } = this.props
+ const txMeta = this.gatherTxMeta()
+ const txParams = txMeta.txParams || {}
+
+ return {
+ from: {
+ address: txParams.from,
+ name: identities[txParams.from].name,
+ },
+ memo: txParams.memo || '',
+ }
+ }
-ConfirmDeployContract.prototype.getData = function () {
- const { identities } = this.props
- const txMeta = this.gatherTxMeta()
- const txParams = txMeta.txParams || {}
+ getAmount () {
+ const { conversionRate, currentCurrency } = this.props
+ const txMeta = this.gatherTxMeta()
+ const txParams = txMeta.txParams || {}
+
+ const FIAT = conversionUtil(txParams.value, {
+ fromNumericBase: 'hex',
+ toNumericBase: 'dec',
+ fromCurrency: 'ETH',
+ toCurrency: currentCurrency,
+ numberOfDecimals: 2,
+ fromDenomination: 'WEI',
+ conversionRate,
+ })
+ const ETH = conversionUtil(txParams.value, {
+ fromNumericBase: 'hex',
+ toNumericBase: 'dec',
+ fromCurrency: 'ETH',
+ toCurrency: 'ETH',
+ fromDenomination: 'WEI',
+ conversionRate,
+ numberOfDecimals: 6,
+ })
+
+ return {
+ fiat: Number(FIAT),
+ token: Number(ETH),
+ }
- return {
- from: {
- address: txParams.from,
- name: identities[txParams.from].name,
- },
- memo: txParams.memo || '',
}
-}
-
-ConfirmDeployContract.prototype.getAmount = function () {
- const { conversionRate, currentCurrency } = this.props
- const txMeta = this.gatherTxMeta()
- const txParams = txMeta.txParams || {}
-
- const FIAT = conversionUtil(txParams.value, {
- fromNumericBase: 'hex',
- toNumericBase: 'dec',
- fromCurrency: 'ETH',
- toCurrency: currentCurrency,
- numberOfDecimals: 2,
- fromDenomination: 'WEI',
- conversionRate,
- })
- const ETH = conversionUtil(txParams.value, {
- fromNumericBase: 'hex',
- toNumericBase: 'dec',
- fromCurrency: 'ETH',
- toCurrency: 'ETH',
- fromDenomination: 'WEI',
- conversionRate,
- numberOfDecimals: 6,
- })
- return {
- fiat: Number(FIAT),
- token: Number(ETH),
+ getGasFee () {
+ const { conversionRate, currentCurrency } = this.props
+ const txMeta = this.gatherTxMeta()
+ const txParams = txMeta.txParams || {}
+
+ // Gas
+ const gas = txParams.gas
+ const gasBn = hexToBn(gas)
+
+ // Gas Price
+ const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_HEX
+ const gasPriceBn = hexToBn(gasPrice)
+
+ const txFeeBn = gasBn.mul(gasPriceBn)
+
+ const FIAT = conversionUtil(txFeeBn, {
+ fromNumericBase: 'BN',
+ toNumericBase: 'dec',
+ fromDenomination: 'WEI',
+ fromCurrency: 'ETH',
+ toCurrency: currentCurrency,
+ numberOfDecimals: 2,
+ conversionRate,
+ })
+ const ETH = conversionUtil(txFeeBn, {
+ fromNumericBase: 'BN',
+ toNumericBase: 'dec',
+ fromDenomination: 'WEI',
+ fromCurrency: 'ETH',
+ toCurrency: 'ETH',
+ numberOfDecimals: 6,
+ conversionRate,
+ })
+
+ return {
+ fiat: Number(FIAT),
+ eth: Number(ETH),
+ }
}
-}
+ renderGasFee () {
+ const { currentCurrency } = this.props
+ const { fiat: fiatGas, eth: ethGas } = this.getGasFee()
-ConfirmDeployContract.prototype.getGasFee = function () {
- const { conversionRate, currentCurrency } = this.props
- const txMeta = this.gatherTxMeta()
- const txParams = txMeta.txParams || {}
+ return (
+ h('section.flex-row.flex-center.confirm-screen-row', [
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', `${fiatGas} ${currentCurrency.toUpperCase()}`),
- // Gas
- const gas = txParams.gas
- const gasBn = hexToBn(gas)
+ h(
+ 'div.confirm-screen-row-detail',
+ `${ethGas} ETH`
+ ),
+ ]),
+ ])
+ )
+ }
- // Gas Price
- const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_HEX
- const gasPriceBn = hexToBn(gasPrice)
+ renderHeroAmount () {
+ const { currentCurrency } = this.props
+ const { fiat: fiatAmount } = this.getAmount()
+ const txMeta = this.gatherTxMeta()
+ const txParams = txMeta.txParams || {}
+ const { memo = '' } = txParams
+
+ return (
+ h('div.confirm-send-token__hero-amount-wrapper', [
+ h('h3.flex-center.confirm-screen-send-amount', `${fiatAmount}`),
+ h('h3.flex-center.confirm-screen-send-amount-currency', currentCurrency.toUpperCase()),
+ h('div.flex-center.confirm-memo-wrapper', [
+ h('h3.confirm-screen-send-memo', memo),
+ ]),
+ ])
+ )
+ }
- const txFeeBn = gasBn.mul(gasPriceBn)
+ renderTotalPlusGas () {
+ const { currentCurrency } = this.props
+ const { fiat: fiatAmount, token: tokenAmount } = this.getAmount()
+ const { fiat: fiatGas, eth: ethGas } = this.getGasFee()
- const FIAT = conversionUtil(txFeeBn, {
- fromNumericBase: 'BN',
- toNumericBase: 'dec',
- fromDenomination: 'WEI',
- fromCurrency: 'ETH',
- toCurrency: currentCurrency,
- numberOfDecimals: 2,
- conversionRate,
- })
- const ETH = conversionUtil(txFeeBn, {
- fromNumericBase: 'BN',
- toNumericBase: 'dec',
- fromDenomination: 'WEI',
- fromCurrency: 'ETH',
- toCurrency: 'ETH',
- numberOfDecimals: 6,
- conversionRate,
- })
+ return (
+ h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
+ h('div.confirm-screen-section-column', [
+ h('span.confirm-screen-label', [ t('total') + ' ' ]),
+ h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]),
+ ]),
- return {
- fiat: Number(FIAT),
- eth: Number(ETH),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', `${fiatAmount + fiatGas} ${currentCurrency.toUpperCase()}`),
+ h('div.confirm-screen-row-detail', `${tokenAmount + ethGas} ETH`),
+ ]),
+ ])
+ )
}
-}
-
-ConfirmDeployContract.prototype.renderGasFee = function () {
- const { currentCurrency } = this.props
- const { fiat: fiatGas, eth: ethGas } = this.getGasFee()
-
- return (
- h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'Gas Fee' ]),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', `${fiatGas} ${currentCurrency.toUpperCase()}`),
-
- h(
- 'div.confirm-screen-row-detail',
- `${ethGas} ETH`
- ),
- ]),
- ])
- )
-}
-ConfirmDeployContract.prototype.renderHeroAmount = function () {
- const { currentCurrency } = this.props
- const { fiat: fiatAmount } = this.getAmount()
- const txMeta = this.gatherTxMeta()
- const txParams = txMeta.txParams || {}
- const { memo = '' } = txParams
-
- return (
- h('div.confirm-send-token__hero-amount-wrapper', [
- h('h3.flex-center.confirm-screen-send-amount', `${fiatAmount}`),
- h('h3.flex-center.confirm-screen-send-amount-currency', currentCurrency.toUpperCase()),
- h('div.flex-center.confirm-memo-wrapper', [
- h('h3.confirm-screen-send-memo', memo),
- ]),
- ])
- )
-}
+ render () {
+ const { backToAccountDetail, selectedAddress } = this.props
+ const txMeta = this.gatherTxMeta()
-ConfirmDeployContract.prototype.renderTotalPlusGas = function () {
- const { currentCurrency } = this.props
- const { fiat: fiatAmount, token: tokenAmount } = this.getAmount()
- const { fiat: fiatGas, eth: ethGas } = this.getGasFee()
-
- return (
- h('section.flex-row.flex-center.confirm-screen-total-box ', [
- h('div.confirm-screen-section-column', [
- h('span.confirm-screen-label', [ 'Total ' ]),
- h('div.confirm-screen-total-box__subtitle', [ 'Amount + Gas' ]),
- ]),
-
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', `${fiatAmount + fiatGas} ${currentCurrency.toUpperCase()}`),
- h('div.confirm-screen-row-detail', `${tokenAmount + ethGas} ETH`),
- ]),
- ])
- )
-}
+ const {
+ from: {
+ address: fromAddress,
+ name: fromName,
+ },
+ } = this.getData()
-ConfirmDeployContract.prototype.render = function () {
- const { backToAccountDetail, selectedAddress } = this.props
- const txMeta = this.gatherTxMeta()
+ this.inputs = []
- const {
- from: {
- address: fromAddress,
- name: fromName,
- },
- } = this.getData()
-
- this.inputs = []
-
- return (
- h('div.flex-column.flex-grow.confirm-screen-container', {
- style: { minWidth: '355px' },
- }, [
- // Main Send token Card
- h('div.confirm-screen-wrapper.flex-column.flex-grow', [
- h('h3.flex-center.confirm-screen-header', [
- h('button.confirm-screen-back-button', {
+ return (
+ h('.page-container', [
+ h('.page-container__header', [
+ h('.page-container__back-button', {
onClick: () => backToAccountDetail(selectedAddress),
- }, 'BACK'),
- h('div.confirm-screen-title', 'Confirm Contract'),
- h('div.confirm-screen-header-tip'),
- ]),
- h('div.flex-row.flex-center.confirm-screen-identicons', [
- h('div.confirm-screen-account-wrapper', [
- h(
- Identicon,
- {
- address: fromAddress,
- diameter: 60,
- },
- ),
- h('span.confirm-screen-account-name', fromName),
- // h('span.confirm-screen-account-number', fromAddress.slice(fromAddress.length - 4)),
- ]),
- h('i.fa.fa-arrow-right.fa-lg'),
- h('div.confirm-screen-account-wrapper', [
- h('i.fa.fa-file-text-o'),
- h('span.confirm-screen-account-name', 'New Contract'),
- h('span.confirm-screen-account-number', ' '),
- ]),
+ }, t('back')),
+ h('.page-container__title', t('confirmContract')),
+ h('.page-container__subtitle', t('pleaseReviewTransaction')),
]),
+ // Main Send token Card
+ h('.page-container__content', [
+
+ h(SenderToRecipient, {
+ senderName: fromName,
+ senderAddress: fromAddress,
+ }),
+
+ // h('h3.flex-center.confirm-screen-sending-to-message', {
+ // style: {
+ // textAlign: 'center',
+ // fontSize: '16px',
+ // },
+ // }, [
+ // `You're deploying a new contract.`,
+ // ]),
+
+ this.renderHeroAmount(),
+
+ h('div.confirm-screen-rows', [
+ h('section.flex-row.flex-center.confirm-screen-row', [
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('from') ]),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', fromName),
+ h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
+ ]),
+ ]),
- // h('h3.flex-center.confirm-screen-sending-to-message', {
- // style: {
- // textAlign: 'center',
- // fontSize: '16px',
- // },
- // }, [
- // `You're deploying a new contract.`,
- // ]),
-
- this.renderHeroAmount(),
-
- h('div.confirm-screen-rows', [
- h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'From' ]),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', fromName),
- h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
+ h('section.flex-row.flex-center.confirm-screen-row', [
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('to') ]),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', t('newContract')),
+ ]),
]),
+
+ this.renderGasFee(),
+
+ this.renderTotalPlusGas(),
+
]),
+ ]),
- h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'To' ]),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', 'New Contract'),
- ]),
+ h('form#pending-tx-form', {
+ onSubmit: event => this.onSubmit(event),
+ }, [
+ h('.page-container__footer', [
+ // Cancel Button
+ h('button.btn-cancel.page-container__footer-button.allcaps', {
+ onClick: event => this.cancel(event, txMeta),
+ }, t('cancel')),
+
+ // Accept Button
+ h('button.btn-confirm.page-container__footer-button.allcaps', {
+ onClick: event => this.onSubmit(event),
+ }, t('confirm')),
]),
+ ]),
+ ])
+ )
+ }
+}
- this.renderGasFee(),
+ConfirmDeployContract.propTypes = {
+ sendTransaction: PropTypes.func,
+ cancelTransaction: PropTypes.func,
+ backToAccountDetail: PropTypes.func,
+ displayWarning: PropTypes.func,
+ identities: PropTypes.object,
+ conversionRate: PropTypes.number,
+ currentCurrency: PropTypes.string,
+ selectedAddress: PropTypes.string,
+}
- this.renderTotalPlusGas(),
+const mapStateToProps = state => {
+ const {
+ conversionRate,
+ identities,
+ currentCurrency,
+ } = state.metamask
+ const accounts = state.metamask.accounts
+ const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
+ return {
+ currentCurrency,
+ conversionRate,
+ identities,
+ selectedAddress,
+ }
+}
- ]),
- ]),
-
- h('form#pending-tx-form', {
- onSubmit: this.onSubmit,
- }, [
- // Cancel Button
- h('div.cancel.btn-light.confirm-screen-cancel-button', {
- onClick: (event) => this.cancel(event, txMeta),
- }, 'CANCEL'),
-
- // Accept Button
- h('button.confirm-screen-confirm-button', ['CONFIRM']),
-
- ]),
- ])
- )
+const mapDispatchToProps = dispatch => {
+ return {
+ backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)),
+ cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
+ displayWarning: warning => actions.displayWarning(t(warning)),
+ }
}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmDeployContract)
diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js
index 3f8d9c28f..f36def9d5 100644
--- a/ui/app/components/pending-tx/confirm-send-ether.js
+++ b/ui/app/components/pending-tx/confirm-send-ether.js
@@ -8,7 +8,13 @@ const Identicon = require('../identicon')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
-const { conversionUtil, addCurrencies } = require('../../conversion-util')
+const {
+ conversionUtil,
+ addCurrencies,
+ multiplyCurrencies,
+} = require('../../conversion-util')
+const GasFeeDisplay = require('../send/gas-fee-display-v2')
+const t = require('../../../i18n')
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
@@ -43,6 +49,7 @@ function mapDispatchToProps (dispatch) {
to,
value: amount,
} = txParams
+
dispatch(actions.updateSend({
gasLimit,
gasPrice,
@@ -55,6 +62,29 @@ function mapDispatchToProps (dispatch) {
dispatch(actions.showSendPage())
},
cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
+ showCustomizeGasModal: (txMeta, sendGasLimit, sendGasPrice, sendGasTotal) => {
+ const { id, txParams, lastGasPrice } = txMeta
+ const { gas: txGasLimit, gasPrice: txGasPrice } = txParams
+
+ let forceGasMin
+ if (lastGasPrice) {
+ forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ toNumericBase: 'hex',
+ fromDenomination: 'WEI',
+ }))
+ }
+
+ dispatch(actions.updateSend({
+ gasLimit: sendGasLimit || txGasLimit,
+ gasPrice: sendGasPrice || txGasPrice,
+ editingTransactionId: id,
+ gasTotal: sendGasTotal,
+ forceGasMin,
+ }))
+ dispatch(actions.showModal({ name: 'CUSTOMIZE_GAS' }))
+ },
}
}
@@ -139,6 +169,7 @@ ConfirmSendEther.prototype.getGasFee = function () {
return {
FIAT,
ETH,
+ gasFeeInHex: txFeeBn.toString(16),
}
}
@@ -146,7 +177,7 @@ ConfirmSendEther.prototype.getData = function () {
const { identities } = this.props
const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {}
- const { FIAT: gasFeeInFIAT, ETH: gasFeeInETH } = this.getGasFee()
+ const { FIAT: gasFeeInFIAT, ETH: gasFeeInETH, gasFeeInHex } = this.getGasFee()
const { FIAT: amountInFIAT, ETH: amountInETH } = this.getAmount()
const totalInFIAT = addCurrencies(gasFeeInFIAT, amountInFIAT, {
@@ -165,7 +196,7 @@ ConfirmSendEther.prototype.getData = function () {
},
to: {
address: txParams.to,
- name: identities[txParams.to] ? identities[txParams.to].name : 'New Recipient',
+ name: identities[txParams.to] ? identities[txParams.to].name : t('newRecipient'),
},
memo: txParams.memo || '',
gasFeeInFIAT,
@@ -174,11 +205,20 @@ ConfirmSendEther.prototype.getData = function () {
amountInETH,
totalInFIAT,
totalInETH,
+ gasFeeInHex,
}
}
ConfirmSendEther.prototype.render = function () {
- const { editTransaction, currentCurrency, clearSend } = this.props
+ const {
+ editTransaction,
+ currentCurrency,
+ clearSend,
+ conversionRate,
+ currentCurrency: convertedCurrency,
+ showCustomizeGasModal,
+ send: { gasTotal, gasLimit: sendGasLimit, gasPrice: sendGasPrice },
+ } = this.props
const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {}
@@ -192,13 +232,17 @@ ConfirmSendEther.prototype.render = function () {
name: toName,
},
memo,
- gasFeeInFIAT,
- gasFeeInETH,
+ gasFeeInHex,
amountInFIAT,
totalInFIAT,
totalInETH,
} = this.getData()
+ const title = txMeta.lastGasPrice ? 'Reprice Transaction' : 'Confirm'
+ const subtitle = txMeta.lastGasPrice
+ ? 'Increase your gas fee to attempt to overwrite and speed up your transaction'
+ : 'Please review your transaction.'
+
// This is from the latest master
// It handles some of the errors that we are not currently handling
// Leaving as comments fo reference
@@ -217,187 +261,193 @@ ConfirmSendEther.prototype.render = function () {
// Main Send token Card
h('div.page-container', [
h('div.page-container__header', [
- h('button.confirm-screen-back-button', {
+ !txMeta.lastGasPrice && h('button.confirm-screen-back-button', {
onClick: () => editTransaction(txMeta),
}, 'Edit'),
- h('div.page-container__title', 'Confirm'),
- h('div.page-container__subtitle', `Please review your transaction.`),
+ h('div.page-container__title', title),
+ h('div.page-container__subtitle', subtitle),
]),
- h('div.flex-row.flex-center.confirm-screen-identicons', [
- h('div.confirm-screen-account-wrapper', [
- h(
- Identicon,
- {
- address: fromAddress,
- diameter: 60,
- },
- ),
- h('span.confirm-screen-account-name', fromName),
- // h('span.confirm-screen-account-number', fromAddress.slice(fromAddress.length - 4)),
+ h('.page-container__content', [
+ h('div.flex-row.flex-center.confirm-screen-identicons', [
+ h('div.confirm-screen-account-wrapper', [
+ h(
+ Identicon,
+ {
+ address: fromAddress,
+ diameter: 60,
+ },
+ ),
+ h('span.confirm-screen-account-name', fromName),
+ // h('span.confirm-screen-account-number', fromAddress.slice(fromAddress.length - 4)),
+ ]),
+ h('i.fa.fa-arrow-right.fa-lg'),
+ h('div.confirm-screen-account-wrapper', [
+ h(
+ Identicon,
+ {
+ address: txParams.to,
+ diameter: 60,
+ },
+ ),
+ h('span.confirm-screen-account-name', toName),
+ // h('span.confirm-screen-account-number', toAddress.slice(toAddress.length - 4)),
+ ]),
]),
- h('i.fa.fa-arrow-right.fa-lg'),
- h('div.confirm-screen-account-wrapper', [
- h(
- Identicon,
- {
- address: txParams.to,
- diameter: 60,
- },
- ),
- h('span.confirm-screen-account-name', toName),
- // h('span.confirm-screen-account-number', toAddress.slice(toAddress.length - 4)),
+
+ // h('h3.flex-center.confirm-screen-sending-to-message', {
+ // style: {
+ // textAlign: 'center',
+ // fontSize: '16px',
+ // },
+ // }, [
+ // `You're sending to Recipient ...${toAddress.slice(toAddress.length - 4)}`,
+ // ]),
+
+ h('h3.flex-center.confirm-screen-send-amount', [`${amountInFIAT}`]),
+ h('h3.flex-center.confirm-screen-send-amount-currency', [ currentCurrency.toUpperCase() ]),
+ h('div.flex-center.confirm-memo-wrapper', [
+ h('h3.confirm-screen-send-memo', [ memo ? `"${memo}"` : '' ]),
]),
- ]),
- // h('h3.flex-center.confirm-screen-sending-to-message', {
- // style: {
- // textAlign: 'center',
- // fontSize: '16px',
- // },
- // }, [
- // `You're sending to Recipient ...${toAddress.slice(toAddress.length - 4)}`,
- // ]),
-
- h('h3.flex-center.confirm-screen-send-amount', [`${amountInFIAT}`]),
- h('h3.flex-center.confirm-screen-send-amount-currency', [ currentCurrency.toUpperCase() ]),
- h('div.flex-center.confirm-memo-wrapper', [
- h('h3.confirm-screen-send-memo', [ memo ? `"${memo}"` : '' ]),
- ]),
+ h('div.confirm-screen-rows', [
+ h('section.flex-row.flex-center.confirm-screen-row', [
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('from') ]),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', fromName),
+ h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
+ ]),
+ ]),
- h('div.confirm-screen-rows', [
- h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'From' ]),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', fromName),
- h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
+ h('section.flex-row.flex-center.confirm-screen-row', [
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('to') ]),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', toName),
+ h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`),
+ ]),
]),
- ]),
- h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'To' ]),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', toName),
- h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`),
+ h('section.flex-row.flex-center.confirm-screen-row', [
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]),
+ h('div.confirm-screen-section-column', [
+ h(GasFeeDisplay, {
+ gasTotal: gasTotal || gasFeeInHex,
+ conversionRate,
+ convertedCurrency,
+ onClick: () => showCustomizeGasModal(txMeta, sendGasLimit, sendGasPrice, gasTotal),
+ }),
+ ]),
]),
- ]),
- h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'Gas Fee' ]),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', `${gasFeeInFIAT} ${currentCurrency.toUpperCase()}`),
+ h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
+ h('div.confirm-screen-section-column', [
+ h('span.confirm-screen-label', [ t('total') + ' ' ]),
+ h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]),
+ ]),
- h('div.confirm-screen-row-detail', `${gasFeeInETH} ETH`),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', `${totalInFIAT} ${currentCurrency.toUpperCase()}`),
+ h('div.confirm-screen-row-detail', `${totalInETH} ETH`),
+ ]),
]),
]),
+ // These are latest errors handling from master
+ // Leaving as comments as reference when we start implementing error handling
+ // h('style', `
+ // .conf-buttons button {
+ // margin-left: 10px;
+ // text-transform: uppercase;
+ // }
+ // `),
+
+ // txMeta.simulationFails ?
+ // h('.error', {
+ // style: {
+ // marginLeft: 50,
+ // fontSize: '0.9em',
+ // },
+ // }, 'Transaction Error. Exception thrown in contract code.')
+ // : null,
+
+ // !isValidAddress ?
+ // h('.error', {
+ // style: {
+ // marginLeft: 50,
+ // fontSize: '0.9em',
+ // },
+ // }, 'Recipient address is invalid. Sending this transaction will result in a loss of ETH.')
+ // : null,
+
+ // insufficientBalance ?
+ // h('span.error', {
+ // style: {
+ // marginLeft: 50,
+ // fontSize: '0.9em',
+ // },
+ // }, 'Insufficient balance for transaction')
+ // : null,
+
+ // // send + cancel
+ // h('.flex-row.flex-space-around.conf-buttons', {
+ // style: {
+ // display: 'flex',
+ // justifyContent: 'flex-end',
+ // margin: '14px 25px',
+ // },
+ // }, [
+ // h('button', {
+ // onClick: (event) => {
+ // this.resetGasFields()
+ // event.preventDefault()
+ // },
+ // }, 'Reset'),
+
+ // // Accept Button or Buy Button
+ // insufficientBalance ? h('button.btn-green', { onClick: props.buyEth }, 'Buy Ether') :
+ // h('input.confirm.btn-green', {
+ // type: 'submit',
+ // value: 'SUBMIT',
+ // style: { marginLeft: '10px' },
+ // disabled: buyDisabled,
+ // }),
+
+ // h('button.cancel.btn-red', {
+ // onClick: props.cancelTransaction,
+ // }, 'Reject'),
+ // ]),
+ // showRejectAll ? h('.flex-row.flex-space-around.conf-buttons', {
+ // style: {
+ // display: 'flex',
+ // justifyContent: 'flex-end',
+ // margin: '14px 25px',
+ // },
+ // }, [
+ // h('button.cancel.btn-red', {
+ // onClick: props.cancelAllTransactions,
+ // }, 'Reject All'),
+ // ]) : null,
+ // ]),
+ // ])
+ // )
+ // }
+ ]),
- h('section.flex-row.flex-center.confirm-screen-total-box ', [
- h('div.confirm-screen-section-column', [
- h('span.confirm-screen-label', [ 'Total ' ]),
- h('div.confirm-screen-total-box__subtitle', [ 'Amount + Gas' ]),
- ]),
+ h('form#pending-tx-form', {
+ onSubmit: this.onSubmit,
+ }, [
+ h('.page-container__footer', [
+ // Cancel Button
+ h('button.btn-cancel.page-container__footer-button.allcaps', {
+ onClick: (event) => {
+ clearSend()
+ this.cancel(event, txMeta)
+ },
+ }, t('cancel')),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', `${totalInFIAT} ${currentCurrency.toUpperCase()}`),
- h('div.confirm-screen-row-detail', `${totalInETH} ETH`),
- ]),
+ // Accept Button
+ h('button.btn-confirm.page-container__footer-button.allcaps', [t('confirm')]),
]),
]),
-
-// These are latest errors handling from master
-// Leaving as comments as reference when we start implementing error handling
-// h('style', `
-// .conf-buttons button {
-// margin-left: 10px;
-// text-transform: uppercase;
-// }
-// `),
-
-// txMeta.simulationFails ?
-// h('.error', {
-// style: {
-// marginLeft: 50,
-// fontSize: '0.9em',
-// },
-// }, 'Transaction Error. Exception thrown in contract code.')
-// : null,
-
-// !isValidAddress ?
-// h('.error', {
-// style: {
-// marginLeft: 50,
-// fontSize: '0.9em',
-// },
-// }, 'Recipient address is invalid. Sending this transaction will result in a loss of ETH.')
-// : null,
-
-// insufficientBalance ?
-// h('span.error', {
-// style: {
-// marginLeft: 50,
-// fontSize: '0.9em',
-// },
-// }, 'Insufficient balance for transaction')
-// : null,
-
-// // send + cancel
-// h('.flex-row.flex-space-around.conf-buttons', {
-// style: {
-// display: 'flex',
-// justifyContent: 'flex-end',
-// margin: '14px 25px',
-// },
-// }, [
-// h('button', {
-// onClick: (event) => {
-// this.resetGasFields()
-// event.preventDefault()
-// },
-// }, 'Reset'),
-
-// // Accept Button or Buy Button
-// insufficientBalance ? h('button.btn-green', { onClick: props.buyEth }, 'Buy Ether') :
-// h('input.confirm.btn-green', {
-// type: 'submit',
-// value: 'SUBMIT',
-// style: { marginLeft: '10px' },
-// disabled: buyDisabled,
-// }),
-
-// h('button.cancel.btn-red', {
-// onClick: props.cancelTransaction,
-// }, 'Reject'),
-// ]),
-// showRejectAll ? h('.flex-row.flex-space-around.conf-buttons', {
-// style: {
-// display: 'flex',
-// justifyContent: 'flex-end',
-// margin: '14px 25px',
-// },
-// }, [
-// h('button.cancel.btn-red', {
-// onClick: props.cancelAllTransactions,
-// }, 'Reject All'),
-// ]) : null,
-// ]),
-// ])
-// )
-// }
- ]),
-
- h('form#pending-tx-form', {
- onSubmit: this.onSubmit,
- }, [
- // Cancel Button
- h('div.cancel.btn-light.confirm-screen-cancel-button', {
- onClick: (event) => {
- clearSend()
- this.cancel(event, txMeta)
- },
- }, 'CANCEL'),
-
- // Accept Button
- h('button.confirm-screen-confirm-button', ['CONFIRM']),
]),
])
)
@@ -412,7 +462,7 @@ ConfirmSendEther.prototype.onSubmit = function (event) {
if (valid && this.verifyGasParams()) {
this.props.sendTransaction(txMeta, event)
} else {
- this.props.dispatch(actions.displayWarning('Invalid Gas Parameters'))
+ this.props.dispatch(actions.displayWarning(t('invalidGasParams')))
this.setState({ submitting: false })
}
}
@@ -445,6 +495,27 @@ ConfirmSendEther.prototype.gatherTxMeta = function () {
const state = this.state
const txData = clone(state.txData) || clone(props.txData)
+ const { gasPrice: sendGasPrice, gas: sendGasLimit } = props.send
+ const {
+ lastGasPrice,
+ txParams: {
+ gasPrice: txGasPrice,
+ gas: txGasLimit,
+ },
+ } = txData
+
+ let forceGasMin
+ if (lastGasPrice) {
+ forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ toNumericBase: 'hex',
+ }))
+ }
+
+ txData.txParams.gasPrice = sendGasPrice || forceGasMin || txGasPrice
+ txData.txParams.gas = sendGasLimit || txGasLimit
+
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
return txData
}
diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js
index dd2bef3da..ccd87c0a4 100644
--- a/ui/app/components/pending-tx/confirm-send-token.js
+++ b/ui/app/components/pending-tx/confirm-send-token.js
@@ -6,8 +6,10 @@ const tokenAbi = require('human-standard-token-abi')
const abiDecoder = require('abi-decoder')
abiDecoder.addABI(tokenAbi)
const actions = require('../../actions')
+const t = require('../../../i18n')
const clone = require('clone')
const Identicon = require('../identicon')
+const GasFeeDisplay = require('../send/gas-fee-display-v2.js')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const {
@@ -88,6 +90,39 @@ function mapDispatchToProps (dispatch, ownProps) {
}))
dispatch(actions.showSendTokenPage())
},
+ showCustomizeGasModal: (txMeta, sendGasLimit, sendGasPrice, sendGasTotal) => {
+ const { id, txParams, lastGasPrice } = txMeta
+ const { gas: txGasLimit, gasPrice: txGasPrice } = txParams
+ const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data)
+ const { params = [] } = tokenData
+ const { value: to } = params[0] || {}
+ const { value: tokenAmountInDec } = params[1] || {}
+ const tokenAmountInHex = conversionUtil(tokenAmountInDec, {
+ fromNumericBase: 'dec',
+ toNumericBase: 'hex',
+ })
+
+ let forceGasMin
+ if (lastGasPrice) {
+ forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ toNumericBase: 'hex',
+ fromDenomination: 'WEI',
+ }))
+ }
+
+ dispatch(actions.updateSend({
+ gasLimit: sendGasLimit || txGasLimit,
+ gasPrice: sendGasPrice || txGasPrice,
+ editingTransactionId: id,
+ gasTotal: sendGasTotal,
+ to,
+ amount: tokenAmountInHex,
+ forceGasMin,
+ }))
+ dispatch(actions.showModal({ name: 'CUSTOMIZE_GAS' }))
+ },
}
}
@@ -133,7 +168,7 @@ ConfirmSendToken.prototype.getAmount = function () {
? +(sendTokenAmount * tokenExchangeRate * conversionRate).toFixed(2)
: null,
token: typeof value === 'undefined'
- ? 'Unknown'
+ ? t('unknown')
: +sendTokenAmount.toFixed(decimals),
}
@@ -187,6 +222,7 @@ ConfirmSendToken.prototype.getGasFee = function () {
token: tokenExchangeRate
? tokenGas
: null,
+ gasFeeInHex: gasTotal.toString(16),
}
}
@@ -204,7 +240,7 @@ ConfirmSendToken.prototype.getData = function () {
},
to: {
address: value,
- name: identities[value] ? identities[value].name : 'New Recipient',
+ name: identities[value] ? identities[value].name : t('newRecipient'),
},
memo: txParams.memo || '',
}
@@ -239,19 +275,25 @@ ConfirmSendToken.prototype.renderHeroAmount = function () {
}
ConfirmSendToken.prototype.renderGasFee = function () {
- const { token: { symbol }, currentCurrency } = this.props
- const { fiat: fiatGas, token: tokenGas, eth: ethGas } = this.getGasFee()
+ const {
+ currentCurrency: convertedCurrency,
+ conversionRate,
+ send: { gasTotal, gasLimit: sendGasLimit, gasPrice: sendGasPrice },
+ showCustomizeGasModal,
+ } = this.props
+ const txMeta = this.gatherTxMeta()
+ const { gasFeeInHex } = this.getGasFee()
return (
h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'Gas Fee' ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]),
h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', `${fiatGas} ${currentCurrency}`),
-
- h(
- 'div.confirm-screen-row-detail',
- tokenGas ? `${tokenGas} ${symbol}` : `${ethGas} ETH`
- ),
+ h(GasFeeDisplay, {
+ gasTotal: gasTotal || gasFeeInHex,
+ conversionRate,
+ convertedCurrency,
+ onClick: () => showCustomizeGasModal(txMeta, sendGasLimit, sendGasPrice, gasTotal),
+ }),
]),
])
)
@@ -264,10 +306,10 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
return fiatAmount && fiatGas
? (
- h('section.flex-row.flex-center.confirm-screen-total-box ', [
+ h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
h('div.confirm-screen-section-column', [
- h('span.confirm-screen-label', [ 'Total ' ]),
- h('div.confirm-screen-total-box__subtitle', [ 'Amount + Gas' ]),
+ h('span.confirm-screen-label', [ t('total') + ' ' ]),
+ h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]),
]),
h('div.confirm-screen-section-column', [
@@ -277,15 +319,15 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
])
)
: (
- h('section.flex-row.flex-center.confirm-screen-total-box ', [
+ h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
h('div.confirm-screen-section-column', [
- h('span.confirm-screen-label', [ 'Total ' ]),
- h('div.confirm-screen-total-box__subtitle', [ 'Amount + Gas' ]),
+ h('span.confirm-screen-label', [ t('total') + ' ' ]),
+ h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]),
]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', `${tokenAmount} ${symbol}`),
- h('div.confirm-screen-row-detail', `+ ${fiatGas} ${currentCurrency} Gas`),
+ h('div.confirm-screen-row-detail', `+ ${fiatGas} ${currentCurrency} ${t('gas')}`),
]),
])
)
@@ -307,91 +349,98 @@ ConfirmSendToken.prototype.render = function () {
this.inputs = []
+ const title = txMeta.lastGasPrice ? 'Reprice Transaction' : t('confirm')
+ const subtitle = txMeta.lastGasPrice
+ ? 'Increase your gas fee to attempt to overwrite and speed up your transaction'
+ : t('pleaseReviewTransaction')
+
return (
h('div.confirm-screen-container.confirm-send-token', [
// Main Send token Card
h('div.page-container', [
h('div.page-container__header', [
- h('button.confirm-screen-back-button', {
+ !txMeta.lastGasPrice && h('button.confirm-screen-back-button', {
onClick: () => editTransaction(txMeta),
- }, 'Edit'),
- h('div.page-container__title', 'Confirm'),
- h('div.page-container__subtitle', `Please review your transaction.`),
+ }, t('edit')),
+ h('div.page-container__title', title),
+ h('div.page-container__subtitle', subtitle),
]),
- h('div.flex-row.flex-center.confirm-screen-identicons', [
- h('div.confirm-screen-account-wrapper', [
- h(
- Identicon,
- {
- address: fromAddress,
- diameter: 60,
- },
- ),
- h('span.confirm-screen-account-name', fromName),
- // h('span.confirm-screen-account-number', fromAddress.slice(fromAddress.length - 4)),
- ]),
- h('i.fa.fa-arrow-right.fa-lg'),
- h('div.confirm-screen-account-wrapper', [
- h(
- Identicon,
- {
- address: toAddress,
- diameter: 60,
- },
- ),
- h('span.confirm-screen-account-name', toName),
- // h('span.confirm-screen-account-number', toAddress.slice(toAddress.length - 4)),
- ]),
- ]),
-
- // h('h3.flex-center.confirm-screen-sending-to-message', {
- // style: {
- // textAlign: 'center',
- // fontSize: '16px',
- // },
- // }, [
- // `You're sending to Recipient ...${toAddress.slice(toAddress.length - 4)}`,
- // ]),
-
- this.renderHeroAmount(),
-
- h('div.confirm-screen-rows', [
- h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'From' ]),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', fromName),
- h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
+ h('.page-container__content', [
+ h('div.flex-row.flex-center.confirm-screen-identicons', [
+ h('div.confirm-screen-account-wrapper', [
+ h(
+ Identicon,
+ {
+ address: fromAddress,
+ diameter: 60,
+ },
+ ),
+ h('span.confirm-screen-account-name', fromName),
+ // h('span.confirm-screen-account-number', fromAddress.slice(fromAddress.length - 4)),
+ ]),
+ h('i.fa.fa-arrow-right.fa-lg'),
+ h('div.confirm-screen-account-wrapper', [
+ h(
+ Identicon,
+ {
+ address: toAddress,
+ diameter: 60,
+ },
+ ),
+ h('span.confirm-screen-account-name', toName),
+ // h('span.confirm-screen-account-number', toAddress.slice(toAddress.length - 4)),
]),
]),
- toAddress && h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'To' ]),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', toName),
- h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`),
+ // h('h3.flex-center.confirm-screen-sending-to-message', {
+ // style: {
+ // textAlign: 'center',
+ // fontSize: '16px',
+ // },
+ // }, [
+ // `You're sending to Recipient ...${toAddress.slice(toAddress.length - 4)}`,
+ // ]),
+
+ this.renderHeroAmount(),
+
+ h('div.confirm-screen-rows', [
+ h('section.flex-row.flex-center.confirm-screen-row', [
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('from') ]),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', fromName),
+ h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
+ ]),
]),
- ]),
- this.renderGasFee(),
+ toAddress && h('section.flex-row.flex-center.confirm-screen-row', [
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('to') ]),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', toName),
+ h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`),
+ ]),
+ ]),
- this.renderTotalPlusGas(),
+ this.renderGasFee(),
- ]),
- ]),
+ this.renderTotalPlusGas(),
- h('form#pending-tx-form', {
- onSubmit: this.onSubmit,
- }, [
- // Cancel Button
- h('div.cancel.btn-light.confirm-screen-cancel-button', {
- onClick: (event) => this.cancel(event, txMeta),
- }, 'CANCEL'),
+ ]),
- // Accept Button
- h('button.confirm-screen-confirm-button', ['CONFIRM']),
+ ]),
+ h('form#pending-tx-form', {
+ onSubmit: this.onSubmit,
+ }, [
+ h('.page-container__footer', [
+ // Cancel Button
+ h('button.btn-cancel.page-container__footer-button.allcaps', {
+ onClick: (event) => this.cancel(event, txMeta),
+ }, t('cancel')),
+
+ // Accept Button
+ h('button.btn-confirm.page-container__footer-button.allcaps', [t('confirm')]),
+ ]),
+ ]),
]),
-
-
])
)
}
@@ -405,7 +454,7 @@ ConfirmSendToken.prototype.onSubmit = function (event) {
if (valid && this.verifyGasParams()) {
this.props.sendTransaction(txMeta, event)
} else {
- this.props.dispatch(actions.displayWarning('Invalid Gas Parameters'))
+ this.props.dispatch(actions.displayWarning(t('invalidGasParams')))
this.setState({ submitting: false })
}
}
@@ -438,6 +487,27 @@ ConfirmSendToken.prototype.gatherTxMeta = function () {
const state = this.state
const txData = clone(state.txData) || clone(props.txData)
+ const { gasPrice: sendGasPrice, gas: sendGasLimit } = props.send
+ const {
+ lastGasPrice,
+ txParams: {
+ gasPrice: txGasPrice,
+ gas: txGasLimit,
+ },
+ } = txData
+
+ let forceGasMin
+ if (lastGasPrice) {
+ forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ toNumericBase: 'hex',
+ }))
+ }
+
+ txData.txParams.gasPrice = sendGasPrice || forceGasMin || txGasPrice
+ txData.txParams.gas = sendGasLimit || txGasLimit
+
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
return txData
}
diff --git a/ui/app/components/pending-typed-msg-details.js b/ui/app/components/pending-typed-msg-details.js
deleted file mode 100644
index b5fd29f71..000000000
--- a/ui/app/components/pending-typed-msg-details.js
+++ /dev/null
@@ -1,59 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-const AccountPanel = require('./account-panel')
-const TypedMessageRenderer = require('./typed-message-renderer')
-
-module.exports = PendingMsgDetails
-
-inherits(PendingMsgDetails, Component)
-function PendingMsgDetails () {
- Component.call(this)
-}
-
-PendingMsgDetails.prototype.render = function () {
- var state = this.props
- var msgData = state.txData
-
- var msgParams = msgData.msgParams || {}
- var address = msgParams.from || state.selectedAddress
- var identity = state.identities[address] || { address: address }
- var account = state.accounts[address] || { address: address }
-
- var { data } = msgParams
-
- return (
- h('div', {
- key: msgData.id,
- style: {
- margin: '10px 20px',
- },
- }, [
-
- // account that will sign
- h(AccountPanel, {
- showFullAddress: true,
- identity: identity,
- account: account,
- imageifyIdenticons: state.imageifyIdenticons,
- }),
-
- // message data
- h('div', {
- style: {
- height: '260px',
- },
- }, [
- h('label.font-small', { style: { display: 'block' } }, 'YOU ARE SIGNING'),
- h(TypedMessageRenderer, {
- value: data,
- style: {
- height: '215px',
- },
- }),
- ]),
-
- ])
- )
-}
diff --git a/ui/app/components/pending-typed-msg.js b/ui/app/components/pending-typed-msg.js
deleted file mode 100644
index f8926d0a3..000000000
--- a/ui/app/components/pending-typed-msg.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const PendingTxDetails = require('./pending-typed-msg-details')
-
-module.exports = PendingMsg
-
-inherits(PendingMsg, Component)
-function PendingMsg () {
- Component.call(this)
-}
-
-PendingMsg.prototype.render = function () {
- var state = this.props
- var msgData = state.txData
-
- return (
-
- h('div', {
- key: msgData.id,
- }, [
-
- // header
- h('h3', {
- style: {
- fontWeight: 'bold',
- textAlign: 'center',
- },
- }, 'Sign Message'),
-
- // message details
- h(PendingTxDetails, state),
-
- // sign + cancel
- h('.flex-row.flex-space-around', [
- h('button', {
- onClick: state.cancelTypedMessage,
- }, 'Cancel'),
- h('button', {
- onClick: state.signTypedMessage,
- }, 'Sign'),
- ]),
- ])
-
- )
-}
diff --git a/ui/app/components/range-slider.js b/ui/app/components/range-slider.js
deleted file mode 100644
index 823f5eb01..000000000
--- a/ui/app/components/range-slider.js
+++ /dev/null
@@ -1,58 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-module.exports = RangeSlider
-
-inherits(RangeSlider, Component)
-function RangeSlider () {
- Component.call(this)
-}
-
-RangeSlider.prototype.render = function () {
- const state = this.state || {}
- const props = this.props
- const onInput = props.onInput || function () {}
- const name = props.name
- const {
- min = 0,
- max = 100,
- increment = 1,
- defaultValue = 50,
- mirrorInput = false,
- } = this.props.options
- const {container, input, range} = props.style
-
- return (
- h('.flex-row', {
- style: container,
- }, [
- h('input', {
- type: 'range',
- name: name,
- min: min,
- max: max,
- step: increment,
- style: range,
- value: state.value || defaultValue,
- onChange: mirrorInput ? this.mirrorInputs.bind(this, event) : onInput,
- }),
-
- // Mirrored input for range
- mirrorInput ? h('input.large-input', {
- type: 'number',
- name: `${name}Mirror`,
- min: min,
- max: max,
- value: state.value || defaultValue,
- step: increment,
- style: input,
- onChange: this.mirrorInputs.bind(this, event),
- }) : null,
- ])
- )
-}
-
-RangeSlider.prototype.mirrorInputs = function (event) {
- this.setState({value: event.target.value})
-}
diff --git a/ui/app/components/send-token/index.js b/ui/app/components/send-token/index.js
deleted file mode 100644
index 99d078251..000000000
--- a/ui/app/components/send-token/index.js
+++ /dev/null
@@ -1,439 +0,0 @@
-const Component = require('react').Component
-const connect = require('react-redux').connect
-const h = require('react-hyperscript')
-const classnames = require('classnames')
-const abi = require('ethereumjs-abi')
-const inherits = require('util').inherits
-const actions = require('../../actions')
-const selectors = require('../../selectors')
-const { isValidAddress, allNull } = require('../../util')
-
-// const BalanceComponent = require('./balance-component')
-const Identicon = require('../identicon')
-const TokenBalance = require('../token-balance')
-const CurrencyToggle = require('../send/currency-toggle')
-const GasTooltip = require('../send/gas-tooltip')
-const GasFeeDisplay = require('../send/gas-fee-display')
-
-module.exports = connect(mapStateToProps, mapDispatchToProps)(SendTokenScreen)
-
-function mapStateToProps (state) {
- // const sidebarOpen = state.appState.sidebarOpen
-
- const { warning } = state.appState
- const identities = state.metamask.identities
- const addressBook = state.metamask.addressBook
- const conversionRate = state.metamask.conversionRate
- const currentBlockGasLimit = state.metamask.currentBlockGasLimit
- const accounts = state.metamask.accounts
- const selectedTokenAddress = state.metamask.selectedTokenAddress
- const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
- const selectedToken = selectors.getSelectedToken(state)
- const tokenExchangeRates = state.metamask.tokenExchangeRates
- const pair = `${selectedToken.symbol.toLowerCase()}_eth`
- const { rate: tokenExchangeRate = 0 } = tokenExchangeRates[pair] || {}
-
- return {
- selectedAddress,
- selectedTokenAddress,
- identities,
- addressBook,
- conversionRate,
- tokenExchangeRate,
- currentBlockGasLimit,
- selectedToken,
- warning,
- }
-}
-
-function mapDispatchToProps (dispatch) {
- return {
- backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)),
- hideWarning: () => dispatch(actions.hideWarning()),
- addToAddressBook: (recipient, nickname) => dispatch(
- actions.addToAddressBook(recipient, nickname)
- ),
- signTx: txParams => dispatch(actions.signTx(txParams)),
- signTokenTx: (tokenAddress, toAddress, amount, txData) => (
- dispatch(actions.signTokenTx(tokenAddress, toAddress, amount, txData))
- ),
- updateTokenExchangeRate: token => dispatch(actions.updateTokenExchangeRate(token)),
- estimateGas: params => dispatch(actions.estimateGas(params)),
- getGasPrice: () => dispatch(actions.getGasPrice()),
- }
-}
-
-inherits(SendTokenScreen, Component)
-function SendTokenScreen () {
- Component.call(this)
- this.state = {
- to: '',
- amount: '0x0',
- amountToSend: '0x0',
- selectedCurrency: 'USD',
- isGasTooltipOpen: false,
- gasPrice: null,
- gasLimit: null,
- errors: {},
- }
-}
-
-SendTokenScreen.prototype.componentWillMount = function () {
- const {
- updateTokenExchangeRate,
- selectedToken: { symbol },
- getGasPrice,
- estimateGas,
- selectedAddress,
- } = this.props
-
- updateTokenExchangeRate(symbol)
-
- const data = Array.prototype.map.call(
- abi.rawEncode(['address', 'uint256'], [selectedAddress, '0x0']),
- x => ('00' + x.toString(16)).slice(-2)
- ).join('')
-
- console.log(data)
- Promise.all([
- getGasPrice(),
- estimateGas({
- from: selectedAddress,
- value: '0x0',
- gas: '746a528800',
- data,
- }),
- ])
- .then(([blockGasPrice, estimatedGas]) => {
- console.log({ blockGasPrice, estimatedGas})
- this.setState({
- gasPrice: blockGasPrice,
- gasLimit: estimatedGas,
- })
- })
-}
-
-SendTokenScreen.prototype.validate = function () {
- const {
- to,
- amount: stringAmount,
- gasPrice: hexGasPrice,
- gasLimit: hexGasLimit,
- } = this.state
-
- const gasPrice = parseInt(hexGasPrice, 16)
- const gasLimit = parseInt(hexGasLimit, 16) / 1000000000
- const amount = Number(stringAmount)
-
- const errors = {
- to: !to ? 'Required' : null,
- amount: !amount ? 'Required' : null,
- gasPrice: !gasPrice ? 'Gas Price Required' : null,
- gasLimit: !gasLimit ? 'Gas Limit Required' : null,
- }
-
- if (to && !isValidAddress(to)) {
- errors.to = 'Invalid address'
- }
-
- const isValid = Object.entries(errors).every(([key, value]) => value === null)
- return {
- isValid,
- errors: isValid ? {} : errors,
- }
-}
-
-SendTokenScreen.prototype.setErrorsFor = function (field) {
- const { errors: previousErrors } = this.state
-
- const {
- isValid,
- errors: newErrors,
- } = this.validate()
-
- const nextErrors = Object.assign({}, previousErrors, {
- [field]: newErrors[field] || null,
- })
-
- if (!isValid) {
- this.setState({
- errors: nextErrors,
- isValid,
- })
- }
-}
-
-SendTokenScreen.prototype.clearErrorsFor = function (field) {
- const { errors: previousErrors } = this.state
- const nextErrors = Object.assign({}, previousErrors, {
- [field]: null,
- })
-
- this.setState({
- errors: nextErrors,
- isValid: allNull(nextErrors),
- })
-}
-
-SendTokenScreen.prototype.getAmountToSend = function (amount, selectedToken) {
- const { decimals } = selectedToken || {}
- const multiplier = Math.pow(10, Number(decimals || 0))
- const sendAmount = '0x' + Number(amount * multiplier).toString(16)
- return sendAmount
-}
-
-SendTokenScreen.prototype.submit = function () {
- const {
- to,
- amount,
- gasPrice,
- gasLimit,
- } = this.state
-
- const {
- identities,
- selectedAddress,
- selectedTokenAddress,
- hideWarning,
- addToAddressBook,
- signTokenTx,
- selectedToken,
- } = this.props
-
- const { nickname = ' ' } = identities[to] || {}
-
- hideWarning()
- addToAddressBook(to, nickname)
-
- const txParams = {
- from: selectedAddress,
- value: '0',
- gas: gasLimit,
- gasPrice: gasPrice,
- }
-
- const sendAmount = this.getAmountToSend(amount, selectedToken)
-
- signTokenTx(selectedTokenAddress, to, sendAmount, txParams)
-}
-
-SendTokenScreen.prototype.renderToAddressInput = function () {
- const {
- identities,
- addressBook,
- } = this.props
-
- const {
- to,
- errors: { to: errorMessage },
- } = this.state
-
- return h('div', {
- className: classnames('send-screen-input-wrapper', {
- 'send-screen-input-wrapper--error': errorMessage,
- }),
- }, [
- h('div', ['To:']),
- h('input.large-input.send-screen-input', {
- name: 'address',
- list: 'addresses',
- placeholder: 'Address',
- value: to,
- onChange: e => this.setState({
- to: e.target.value,
- errors: {},
- }),
- onBlur: () => {
- this.setErrorsFor('to')
- },
- onFocus: event => {
- if (to) event.target.select()
- this.clearErrorsFor('to')
- },
- }),
- h('datalist#addresses', [
- // Corresponds to the addresses owned.
- Object.entries(identities).map(([key, { address, name }]) => {
- return h('option', {
- value: address,
- label: name,
- key: address,
- })
- }),
- addressBook.map(({ address, name }) => {
- return h('option', {
- value: address,
- label: name,
- key: address,
- })
- }),
- ]),
- h('div.send-screen-input-wrapper__error-message', [ errorMessage ]),
- ])
-}
-
-SendTokenScreen.prototype.renderAmountInput = function () {
- const {
- selectedCurrency,
- amount,
- errors: { amount: errorMessage },
- } = this.state
-
- const {
- tokenExchangeRate,
- selectedToken: {symbol},
- } = this.props
-
- return h('div.send-screen-input-wrapper', {
- className: classnames('send-screen-input-wrapper', {
- 'send-screen-input-wrapper--error': errorMessage,
- }),
- }, [
- h('div.send-screen-amount-labels', [
- h('span', ['Amount']),
- h(CurrencyToggle, {
- currentCurrency: tokenExchangeRate ? selectedCurrency : 'USD',
- currencies: tokenExchangeRate ? [ symbol, 'USD' ] : [],
- onClick: currency => this.setState({ selectedCurrency: currency }),
- }),
- ]),
- h('input.large-input.send-screen-input', {
- placeholder: `0 ${symbol}`,
- type: 'number',
- value: amount,
- onChange: e => this.setState({
- amount: e.target.value,
- }),
- onBlur: () => {
- this.setErrorsFor('amount')
- },
- onFocus: () => this.clearErrorsFor('amount'),
- }),
- h('div.send-screen-input-wrapper__error-message', [ errorMessage ]),
- ])
-}
-
-SendTokenScreen.prototype.renderGasInput = function () {
- const {
- isGasTooltipOpen,
- gasPrice,
- gasLimit,
- selectedCurrency,
- errors: {
- gasPrice: gasPriceErrorMessage,
- gasLimit: gasLimitErrorMessage,
- },
- } = this.state
-
- const {
- conversionRate,
- tokenExchangeRate,
- currentBlockGasLimit,
- } = this.props
-
- return h('div.send-screen-input-wrapper', {
- className: classnames('send-screen-input-wrapper', {
- 'send-screen-input-wrapper--error': gasPriceErrorMessage || gasLimitErrorMessage,
- }),
- }, [
- isGasTooltipOpen && h(GasTooltip, {
- className: 'send-tooltip',
- gasPrice: gasPrice || '0x0',
- gasLimit: gasLimit || '0x0',
- onClose: () => this.setState({ isGasTooltipOpen: false }),
- onFeeChange: ({ gasLimit, gasPrice }) => {
- this.setState({ gasLimit, gasPrice, errors: {} })
- },
- onBlur: () => {
- this.setErrorsFor('gasLimit')
- this.setErrorsFor('gasPrice')
- },
- onFocus: () => {
- this.clearErrorsFor('gasLimit')
- this.clearErrorsFor('gasPrice')
- },
- }),
-
- h('div.send-screen-gas-labels', {}, [
- h('span', [ h('i.fa.fa-bolt'), 'Gas fee:']),
- h('span', ['What\'s this?']),
- ]),
- h('div.large-input.send-screen-gas-input', [
- h(GasFeeDisplay, {
- conversionRate,
- tokenExchangeRate,
- gasPrice: gasPrice || '0x0',
- activeCurrency: selectedCurrency,
- gas: gasLimit || '0x0',
- blockGasLimit: currentBlockGasLimit,
- }),
- h(
- 'div.send-screen-gas-input-customize',
- { onClick: () => this.setState({ isGasTooltipOpen: !isGasTooltipOpen }) },
- ['Customize']
- ),
- ]),
- h('div.send-screen-input-wrapper__error-message', [
- gasPriceErrorMessage || gasLimitErrorMessage,
- ]),
- ])
-}
-
-SendTokenScreen.prototype.renderMemoInput = function () {
- return h('div.send-screen-input-wrapper', [
- h('div', {}, ['Transaction memo (optional)']),
- h(
- 'input.large-input.send-screen-input',
- { onChange: e => this.setState({ memo: e.target.value }) }
- ),
- ])
-}
-
-SendTokenScreen.prototype.renderButtons = function () {
- const { selectedAddress, backToAccountDetail } = this.props
- const { isValid } = this.validate()
-
- return h('div.send-token__button-group', [
- h('button.send-token__button-next.btn-secondary', {
- className: !isValid && 'send-screen__send-button__disabled',
- onClick: () => isValid && this.submit(),
- }, ['Next']),
- h('button.send-token__button-cancel.btn-tertiary', {
- onClick: () => backToAccountDetail(selectedAddress),
- }, ['Cancel']),
- ])
-}
-
-SendTokenScreen.prototype.render = function () {
- const {
- selectedTokenAddress,
- selectedToken,
- warning,
- } = this.props
-
- return h('div.send-token', [
- h('div.send-token__content', [
- h(Identicon, {
- diameter: 75,
- address: selectedTokenAddress,
- }),
- h('div.send-token__title', ['Send Tokens']),
- h('div.send-token__description', ['Send Tokens to anyone with an Ethereum account']),
- h('div.send-token__balance-text', ['Your Token Balance is:']),
- h('div.send-token__token-balance', [
- h(TokenBalance, { token: selectedToken, balanceOnly: true }),
- ]),
- h('div.send-token__token-symbol', [selectedToken.symbol]),
- this.renderToAddressInput(),
- this.renderAmountInput(),
- this.renderGasInput(),
- this.renderMemoInput(),
- warning && h('div.send-screen-input-wrapper--error', {},
- h('div.send-screen-input-wrapper__error-message', [
- warning,
- ])
- ),
- ]),
- this.renderButtons(),
- ])
-}
diff --git a/ui/app/components/send/currency-toggle.js b/ui/app/components/send/currency-toggle.js
deleted file mode 100644
index 7aaccd490..000000000
--- a/ui/app/components/send/currency-toggle.js
+++ /dev/null
@@ -1,44 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const classnames = require('classnames')
-
-module.exports = CurrencyToggle
-
-inherits(CurrencyToggle, Component)
-function CurrencyToggle () {
- Component.call(this)
-}
-
-const defaultCurrencies = [ 'ETH', 'USD' ]
-
-CurrencyToggle.prototype.renderToggles = function () {
- const { onClick, activeCurrency } = this.props
- const [currencyA, currencyB] = this.props.currencies || defaultCurrencies
-
- return [
- h('span', {
- className: classnames('currency-toggle__item', {
- 'currency-toggle__item--selected': currencyA === activeCurrency,
- }),
- onClick: () => onClick(currencyA),
- }, [ currencyA ]),
- '<>',
- h('span', {
- className: classnames('currency-toggle__item', {
- 'currency-toggle__item--selected': currencyB === activeCurrency,
- }),
- onClick: () => onClick(currencyB),
- }, [ currencyB ]),
- ]
-}
-
-CurrencyToggle.prototype.render = function () {
- const currencies = this.props.currencies || defaultCurrencies
-
- return h('span.currency-toggle', currencies.length
- ? this.renderToggles()
- : []
- )
-}
-
diff --git a/ui/app/components/send/eth-fee-display.js b/ui/app/components/send/eth-fee-display.js
deleted file mode 100644
index 9eda5ec62..000000000
--- a/ui/app/components/send/eth-fee-display.js
+++ /dev/null
@@ -1,37 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const EthBalance = require('../eth-balance')
-const { getTxFeeBn } = require('../../util')
-
-module.exports = EthFeeDisplay
-
-inherits(EthFeeDisplay, Component)
-function EthFeeDisplay () {
- Component.call(this)
-}
-
-EthFeeDisplay.prototype.render = function () {
- const {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- } = this.props
-
- return h(EthBalance, {
- value: getTxFeeBn(gas, gasPrice, blockGasLimit),
- currentCurrency: activeCurrency,
- conversionRate,
- showFiat: false,
- hideTooltip: true,
- styleOveride: {
- color: '#5d5d5d',
- fontSize: '16px',
- fontFamily: 'DIN OT',
- lineHeight: '22.4px',
- },
- })
-}
-
diff --git a/ui/app/components/send/gas-fee-display-v2.js b/ui/app/components/send/gas-fee-display-v2.js
index 0c4c3f7a9..f6af13454 100644
--- a/ui/app/components/send/gas-fee-display-v2.js
+++ b/ui/app/components/send/gas-fee-display-v2.js
@@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const CurrencyDisplay = require('./currency-display')
+const t = require('../../../i18n')
module.exports = GasFeeDisplay
@@ -17,6 +18,7 @@ GasFeeDisplay.prototype.render = function () {
onClick,
primaryCurrency = 'ETH',
convertedCurrency,
+ gasLoadingError,
} = this.props
return h('div.send-v2__gas-fee-display', [
@@ -30,15 +32,16 @@ GasFeeDisplay.prototype.render = function () {
convertedPrefix: '$',
readOnly: true,
})
- : h('div.currency-display', 'Loading...'),
+ : gasLoadingError
+ ? h('div..currency-display.currency-display--message', 'Set with the gas price customizer.')
+ : h('div.currency-display', t('loading')),
- h('button.send-v2__sliders-icon-container', {
+ h('button.sliders-icon-container', {
onClick,
- disabled: !gasTotal,
+ disabled: !gasTotal && !gasLoadingError,
}, [
- h('i.fa.fa-sliders.send-v2__sliders-icon'),
+ h('i.fa.fa-sliders.sliders-icon'),
]),
])
}
-
diff --git a/ui/app/components/send/gas-fee-display.js b/ui/app/components/send/gas-fee-display.js
deleted file mode 100644
index a9a3f3f49..000000000
--- a/ui/app/components/send/gas-fee-display.js
+++ /dev/null
@@ -1,62 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const USDFeeDisplay = require('./usd-fee-display')
-const EthFeeDisplay = require('./eth-fee-display')
-const { getTxFeeBn, formatBalance, shortenBalance } = require('../../util')
-
-module.exports = GasFeeDisplay
-
-inherits(GasFeeDisplay, Component)
-function GasFeeDisplay () {
- Component.call(this)
-}
-
-GasFeeDisplay.prototype.getTokenValue = function () {
- const {
- tokenExchangeRate,
- gas,
- gasPrice,
- blockGasLimit,
- } = this.props
-
- const value = formatBalance(getTxFeeBn(gas, gasPrice, blockGasLimit), 6, true)
- const [ethNumber] = value.split(' ')
-
- return shortenBalance(Number(ethNumber) / tokenExchangeRate, 6)
-}
-
-GasFeeDisplay.prototype.render = function () {
- const {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- } = this.props
-
- switch (activeCurrency) {
- case 'USD':
- return h(USDFeeDisplay, {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- })
- case 'ETH':
- return h(EthFeeDisplay, {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- })
- default:
- return h('div.token-gas', [
- h('div.token-gas__amount', this.getTokenValue()),
- h('div.token-gas__symbol', activeCurrency),
- ])
- }
-}
-
diff --git a/ui/app/components/send/gas-tooltip.js b/ui/app/components/send/gas-tooltip.js
index 46aff3499..d925d3ed8 100644
--- a/ui/app/components/send/gas-tooltip.js
+++ b/ui/app/components/send/gas-tooltip.js
@@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const InputNumber = require('../input-number.js')
+const t = require('../../../i18n')
module.exports = GasTooltip
@@ -81,7 +82,7 @@ GasTooltip.prototype.render = function () {
'marginTop': '81px',
},
}, [
- h('span.gas-tooltip-label', {}, ['Gas Limit']),
+ h('span.gas-tooltip-label', {}, [t('gasLimit')]),
h('i.fa.fa-info-circle'),
]),
h(InputNumber, {
@@ -97,4 +98,3 @@ GasTooltip.prototype.render = function () {
]),
])
}
-
diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js
index 1106902b7..d1319b6dc 100644
--- a/ui/app/components/send/send-v2-container.js
+++ b/ui/app/components/send/send-v2-container.js
@@ -48,6 +48,7 @@ function mapStateToProps (state) {
primaryCurrency,
convertedCurrency: getCurrentCurrency(state),
data,
+ selectedAddress,
amountConversionRate: selectedToken ? tokenToFiatRate : conversionRate,
tokenContract: getSelectedTokenContract(state),
unapprovedTxs: state.metamask.unapprovedTxs,
diff --git a/ui/app/components/send/to-autocomplete.js b/ui/app/components/send/to-autocomplete.js
index e0cdd0a58..72074229e 100644
--- a/ui/app/components/send/to-autocomplete.js
+++ b/ui/app/components/send/to-autocomplete.js
@@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const AccountListItem = require('./account-list-item')
+const t = require('../../../i18n')
module.exports = ToAutoComplete
@@ -92,7 +93,7 @@ ToAutoComplete.prototype.render = function () {
return h('div.send-v2__to-autocomplete', {}, [
h('input.send-v2__to-autocomplete__input', {
- placeholder: 'Recipient Address',
+ placeholder: t('recipientAddress'),
className: inError ? `send-v2__error-border` : '',
value: to,
onChange: event => onChange(event.target.value),
@@ -111,4 +112,3 @@ ToAutoComplete.prototype.render = function () {
])
}
-
diff --git a/ui/app/components/send/usd-fee-display.js b/ui/app/components/send/usd-fee-display.js
deleted file mode 100644
index 4cf31a493..000000000
--- a/ui/app/components/send/usd-fee-display.js
+++ /dev/null
@@ -1,35 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const FiatValue = require('../fiat-value')
-const { getTxFeeBn } = require('../../util')
-
-module.exports = USDFeeDisplay
-
-inherits(USDFeeDisplay, Component)
-function USDFeeDisplay () {
- Component.call(this)
-}
-
-USDFeeDisplay.prototype.render = function () {
- const {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- } = this.props
-
- return h(FiatValue, {
- value: getTxFeeBn(gas, gasPrice, blockGasLimit),
- conversionRate,
- currentCurrency: activeCurrency,
- style: {
- color: '#5d5d5d',
- fontSize: '16px',
- fontFamily: 'DIN OT',
- lineHeight: '22.4px',
- },
- })
-}
-
diff --git a/ui/app/components/sender-to-recipient.js b/ui/app/components/sender-to-recipient.js
new file mode 100644
index 000000000..25b9c7d6b
--- /dev/null
+++ b/ui/app/components/sender-to-recipient.js
@@ -0,0 +1,45 @@
+const { Component } = require('react')
+const h = require('react-hyperscript')
+const PropTypes = require('prop-types')
+const t = require('../../i18n')
+const Identicon = require('./identicon')
+
+class SenderToRecipient extends Component {
+ render () {
+ const { senderName, senderAddress } = this.props
+
+ return (
+ h('.sender-to-recipient__container', [
+ h('.sender-to-recipient__sender', [
+ h('.sender-to-recipient__sender-icon', [
+ h(Identicon, {
+ address: senderAddress,
+ diameter: 20,
+ }),
+ ]),
+ h('.sender-to-recipient__name.sender-to-recipient__sender-name', senderName),
+ ]),
+ h('.sender-to-recipient__arrow-container', [
+ h('.sender-to-recipient__arrow-circle', [
+ h('img', {
+ height: 15,
+ width: 15,
+ src: '/images/arrow-right.svg',
+ }),
+ ]),
+ ]),
+ h('.sender-to-recipient__recipient', [
+ h('i.fa.fa-file-text-o'),
+ h('.sender-to-recipient__name.sender-to-recipient__recipient-name', t('newContract')),
+ ]),
+ ])
+ )
+ }
+}
+
+SenderToRecipient.propTypes = {
+ senderName: PropTypes.string,
+ senderAddress: PropTypes.string,
+}
+
+module.exports = SenderToRecipient
diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js
index 87eb1588a..3f8c17932 100644
--- a/ui/app/components/shapeshift-form.js
+++ b/ui/app/components/shapeshift-form.js
@@ -7,6 +7,7 @@ const { qrcode } = require('qrcode-npm')
const { shapeShiftSubview, pairUpdate, buyWithShapeShift } = require('../actions')
const { isValidAddress } = require('../util')
const SimpleDropdown = require('./dropdowns/simple-dropdown')
+const t = require('../../i18n')
function mapStateToProps (state) {
const {
@@ -15,7 +16,7 @@ function mapStateToProps (state) {
selectedAddress,
} = state.metamask
const { warning } = state.appState
-
+
return {
coinOptions,
tokenExchangeRates,
@@ -93,7 +94,7 @@ ShapeshiftForm.prototype.onBuyWithShapeShift = function () {
}))
.catch(() => this.setState({
showQrCode: false,
- errorMessage: 'Invalid Request',
+ errorMessage: t('invalidRequest'),
isLoading: false,
}))
}
@@ -125,10 +126,10 @@ ShapeshiftForm.prototype.renderMarketInfo = function () {
return h('div.shapeshift-form__metadata', {}, [
- this.renderMetadata('Status', limit ? 'Available' : 'Unavailable'),
- this.renderMetadata('Limit', limit),
- this.renderMetadata('Exchange Rate', rate),
- this.renderMetadata('Minimum', minimum),
+ this.renderMetadata(t('status'), limit ? t('available') : t('unavailable')),
+ this.renderMetadata(t('limit'), limit),
+ this.renderMetadata(t('exchangeRate'), rate),
+ this.renderMetadata(t('min'), minimum),
])
}
@@ -142,7 +143,7 @@ ShapeshiftForm.prototype.renderQrCode = function () {
return h('div.shapeshift-form', {}, [
h('div.shapeshift-form__deposit-instruction', [
- `Deposit your ${depositCoin.toUpperCase()} to the address below:`,
+ t('depositCoin', [depositCoin.toUpperCase()]),
]),
h('div', depositAddress),
@@ -179,7 +180,7 @@ ShapeshiftForm.prototype.render = function () {
h('div.shapeshift-form__selector', [
- h('div.shapeshift-form__selector-label', 'Deposit'),
+ h('div.shapeshift-form__selector-label', t('deposit')),
h(SimpleDropdown, {
selectedOption: this.state.depositCoin,
@@ -199,7 +200,7 @@ ShapeshiftForm.prototype.render = function () {
h('div.shapeshift-form__selector', [
h('div.shapeshift-form__selector-label', [
- 'Receive',
+ t('receive'),
]),
h('div.shapeshift-form__selector-input', ['ETH']),
@@ -217,7 +218,7 @@ ShapeshiftForm.prototype.render = function () {
}, [
h('div.shapeshift-form__address-input-label', [
- 'Your Refund Address',
+ t('refundAddress'),
]),
h('input.shapeshift-form__address-input', {
@@ -239,7 +240,7 @@ ShapeshiftForm.prototype.render = function () {
className: btnClass,
disabled: !token,
onClick: () => this.onBuyWithShapeShift(),
- }, ['Buy']),
+ }, [t('buy')]),
])
}
diff --git a/ui/app/components/shift-list-item.js b/ui/app/components/shift-list-item.js
index 017bf9f0c..fddbc6821 100644
--- a/ui/app/components/shift-list-item.js
+++ b/ui/app/components/shift-list-item.js
@@ -6,6 +6,7 @@ const vreme = new (require('vreme'))()
const explorerLink = require('etherscan-link').createExplorerLink
const actions = require('../actions')
const addressSummary = require('../util').addressSummary
+const t = require('../../i18n')
const CopyButton = require('./copyButton')
const EthBalance = require('./eth-balance')
@@ -75,7 +76,7 @@ ShiftListItem.prototype.renderUtilComponents = function () {
value: this.props.depositAddress,
}),
h(Tooltip, {
- title: 'QR Code',
+ title: t('qrCode'),
}, [
h('i.fa.fa-qrcode.pointer.pop-hover', {
onClick: () => props.dispatch(actions.reshowQrCode(props.depositAddress, props.depositType)),
@@ -135,8 +136,8 @@ ShiftListItem.prototype.renderInfo = function () {
color: '#ABA9AA',
width: '100%',
},
- }, `${props.depositType} to ETH via ShapeShift`),
- h('div', 'No deposits received'),
+ }, t('toETHviaShapeShift', [props.depositType])),
+ h('div', t('noDeposits')),
h('div', {
style: {
fontSize: 'x-small',
@@ -158,8 +159,8 @@ ShiftListItem.prototype.renderInfo = function () {
color: '#ABA9AA',
width: '100%',
},
- }, `${props.depositType} to ETH via ShapeShift`),
- h('div', 'Conversion in progress'),
+ }, t('toETHviaShapeShift', [props.depositType])),
+ h('div', t('conversionProgress')),
h('div', {
style: {
fontSize: 'x-small',
@@ -184,7 +185,7 @@ ShiftListItem.prototype.renderInfo = function () {
color: '#ABA9AA',
width: '100%',
},
- }, 'From ShapeShift'),
+ }, t('fromShapeShift')),
h('div', formatDate(props.time)),
h('div', {
style: {
@@ -196,7 +197,7 @@ ShiftListItem.prototype.renderInfo = function () {
])
case 'failed':
- return h('span.error', '(Failed)')
+ return h('span.error', '(' + t('failed') + ')')
default:
return ''
}
diff --git a/ui/app/components/signature-request.js b/ui/app/components/signature-request.js
index c5cc23aa8..7bf34e7b6 100644
--- a/ui/app/components/signature-request.js
+++ b/ui/app/components/signature-request.js
@@ -9,6 +9,7 @@ const classnames = require('classnames')
const AccountDropdownMini = require('./dropdowns/account-dropdown-mini')
const actions = require('../actions')
+const t = require('../../i18n')
const { conversionUtil } = require('../conversion-util')
const {
@@ -54,7 +55,7 @@ SignatureRequest.prototype.renderHeader = function () {
h('div.request-signature__header-background'),
- h('div.request-signature__header__text', 'Signature Request'),
+ h('div.request-signature__header__text', t('sigRequest')),
h('div.request-signature__header__tip-container', [
h('div.request-signature__header__tip'),
@@ -75,7 +76,7 @@ SignatureRequest.prototype.renderAccountDropdown = function () {
return h('div.request-signature__account', [
- h('div.request-signature__account-text', ['Account:']),
+ h('div.request-signature__account-text', [t('account') + ':']),
h(AccountDropdownMini, {
selectedAccount,
@@ -102,7 +103,7 @@ SignatureRequest.prototype.renderBalance = function () {
return h('div.request-signature__balance', [
- h('div.request-signature__balance-text', ['Balance:']),
+ h('div.request-signature__balance-text', [t('balance')]),
h('div.request-signature__balance-value', `${balanceInEther} ETH`),
@@ -136,7 +137,7 @@ SignatureRequest.prototype.renderRequestInfo = function () {
return h('div.request-signature__request-info', [
h('div.request-signature__headline', [
- `Your signature is being requested`,
+ t('yourSigRequested'),
]),
])
@@ -154,21 +155,18 @@ SignatureRequest.prototype.msgHexToText = function (hex) {
SignatureRequest.prototype.renderBody = function () {
let rows
- let notice = 'You are signing:'
+ let notice = t('youSign') + ':'
const { txData } = this.props
const { type, msgParams: { data } } = txData
if (type === 'personal_sign') {
- rows = [{ name: 'Message', value: this.msgHexToText(data) }]
+ rows = [{ name: t('message'), value: this.msgHexToText(data) }]
} else if (type === 'eth_signTypedData') {
rows = data
} else if (type === 'eth_sign') {
- rows = [{ name: 'Message', value: data }]
- notice = `Signing this message can have
- dangerous side effects. Only sign messages from
- sites you fully trust with your entire account.
- This dangerous method will be removed in a future version. `
+ rows = [{ name: t('message'), value: data }]
+ notice = t('signNotice')
}
return h('div.request-signature__body', {}, [
@@ -227,10 +225,10 @@ SignatureRequest.prototype.renderFooter = function () {
return h('div.request-signature__footer', [
h('button.request-signature__footer__cancel-button', {
onClick: cancel,
- }, 'CANCEL'),
+ }, t('cancel')),
h('button.request-signature__footer__sign-button', {
onClick: sign,
- }, 'SIGN'),
+ }, t('sign')),
])
}
@@ -250,4 +248,3 @@ SignatureRequest.prototype.render = function () {
)
}
-
diff --git a/ui/app/components/template.js b/ui/app/components/template.js
deleted file mode 100644
index b6ed8eaa0..000000000
--- a/ui/app/components/template.js
+++ /dev/null
@@ -1,18 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-module.exports = NewComponent
-
-inherits(NewComponent, Component)
-function NewComponent () {
- Component.call(this)
-}
-
-NewComponent.prototype.render = function () {
- const props = this.props
-
- return (
- h('span', props.message)
- )
-}
diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js
index 8e06e0f27..01529aeda 100644
--- a/ui/app/components/token-list.js
+++ b/ui/app/components/token-list.js
@@ -5,6 +5,7 @@ const TokenTracker = require('eth-token-tracker')
const TokenCell = require('./token-cell.js')
const connect = require('react-redux').connect
const selectors = require('../selectors')
+const t = require('../../i18n')
function mapStateToProps (state) {
return {
@@ -42,7 +43,7 @@ TokenList.prototype.render = function () {
const { tokens, isLoading, error } = state
if (isLoading) {
- return this.message('Loading Tokens...')
+ return this.message(t('loadingTokens'))
}
if (error) {
@@ -52,7 +53,7 @@ TokenList.prototype.render = function () {
padding: '80px',
},
}, [
- 'We had trouble loading your token balances. You can view them ',
+ t('troubleTokenBalances'),
h('span.hotFix', {
style: {
color: 'rgba(247, 134, 28, 1)',
@@ -63,7 +64,7 @@ TokenList.prototype.render = function () {
url: `https://ethplorer.io/address/${userAddress}`,
})
},
- }, 'here'),
+ }, t('here')),
])
}
diff --git a/ui/app/components/transaction-list-item-icon.js b/ui/app/components/transaction-list-item-icon.js
deleted file mode 100644
index f442b05af..000000000
--- a/ui/app/components/transaction-list-item-icon.js
+++ /dev/null
@@ -1,68 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const Tooltip = require('./tooltip')
-
-const Identicon = require('./identicon')
-
-module.exports = TransactionIcon
-
-inherits(TransactionIcon, Component)
-function TransactionIcon () {
- Component.call(this)
-}
-
-TransactionIcon.prototype.render = function () {
- const { transaction, txParams, isMsg } = this.props
- switch (transaction.status) {
- case 'unapproved':
- return h(!isMsg ? '.unapproved-tx-icon' : 'i.fa.fa-certificate.fa-lg')
-
- case 'rejected':
- return h('i.fa.fa-exclamation-triangle.fa-lg.warning', {
- style: {
- width: '24px',
- },
- })
-
- case 'failed':
- return h('i.fa.fa-exclamation-triangle.fa-lg.error', {
- style: {
- width: '24px',
- },
- })
-
- case 'submitted':
- return h(Tooltip, {
- title: 'Pending',
- position: 'right',
- }, [
- h('i.fa.fa-ellipsis-h', {
- style: {
- fontSize: '27px',
- },
- }),
- ])
- }
-
- if (isMsg) {
- return h('i.fa.fa-certificate.fa-lg', {
- style: {
- width: '24px',
- },
- })
- }
-
- if (txParams.to) {
- return h(Identicon, {
- diameter: 24,
- address: txParams.to || transaction.hash,
- })
- } else {
- return h('i.fa.fa-file-text-o.fa-lg', {
- style: {
- width: '24px',
- },
- })
- }
-}
diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js
deleted file mode 100644
index a45cd441a..000000000
--- a/ui/app/components/transaction-list-item.js
+++ /dev/null
@@ -1,238 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const connect = require('react-redux').connect
-
-const EthBalance = require('./eth-balance')
-const addressSummary = require('../util').addressSummary
-const explorerLink = require('etherscan-link').createExplorerLink
-const CopyButton = require('./copyButton')
-const vreme = new (require('vreme'))()
-const Tooltip = require('./tooltip')
-const numberToBN = require('number-to-bn')
-const actions = require('../actions')
-
-const TransactionIcon = require('./transaction-list-item-icon')
-const ShiftListItem = require('./shift-list-item')
-
-const mapDispatchToProps = dispatch => {
- return {
- retryTransaction: transactionId => dispatch(actions.retryTransaction(transactionId)),
- }
-}
-
-module.exports = connect(null, mapDispatchToProps)(TransactionListItem)
-
-inherits(TransactionListItem, Component)
-function TransactionListItem () {
- Component.call(this)
-}
-
-TransactionListItem.prototype.showRetryButton = function () {
- const { transaction = {} } = this.props
- const { status, time } = transaction
- return status === 'submitted' && Date.now() - time > 30000
-}
-
-TransactionListItem.prototype.render = function () {
- const { transaction, network, conversionRate, currentCurrency } = this.props
- const { status } = transaction
- if (transaction.key === 'shapeshift') {
- if (network === '1') return h(ShiftListItem, transaction)
- }
- var date = formatDate(transaction.time)
-
- let isLinkable = false
- const numericNet = parseInt(network)
- isLinkable = numericNet === 1 || numericNet === 3 || numericNet === 4 || numericNet === 42
-
- var isMsg = ('msgParams' in transaction)
- var isTx = ('txParams' in transaction)
- var isPending = status === 'unapproved'
- let txParams
- if (isTx) {
- txParams = transaction.txParams
- } else if (isMsg) {
- txParams = transaction.msgParams
- }
-
- const nonce = txParams.nonce ? numberToBN(txParams.nonce).toString(10) : ''
-
- const isClickable = ('hash' in transaction && isLinkable) || isPending
- return (
- h('.transaction-list-item.flex-column', {
- onClick: (event) => {
- if (isPending) {
- this.props.showTx(transaction.id)
- }
- event.stopPropagation()
- if (!transaction.hash || !isLinkable) return
- var url = explorerLink(transaction.hash, parseInt(network))
- global.platform.openWindow({ url })
- },
- style: {
- padding: '20px 0',
- alignItems: 'center',
- },
- }, [
- h(`.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
- style: {
- width: '100%',
- },
- }, [
- h('.identicon-wrapper.flex-column.flex-center.select-none', [
- h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
- ]),
-
- h(Tooltip, {
- title: 'Transaction Number',
- position: 'right',
- }, [
- h('span', {
- style: {
- display: 'flex',
- cursor: 'normal',
- flexDirection: 'column',
- alignItems: 'center',
- justifyContent: 'center',
- padding: '10px',
- },
- }, nonce),
- ]),
-
- h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [
- domainField(txParams),
- h('div', date),
- recipientField(txParams, transaction, isTx, isMsg),
- ]),
-
- // Places a copy button if tx is successful, else places a placeholder empty div.
- transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}),
-
- isTx ? h(EthBalance, {
- value: txParams.value,
- conversionRate,
- currentCurrency,
- width: '55px',
- shorten: true,
- showFiat: false,
- style: {fontSize: '15px'},
- }) : h('.flex-column'),
- ]),
-
- this.showRetryButton() && h('.transition-list-item__retry.grow-on-hover', {
- onClick: event => {
- event.stopPropagation()
- this.resubmit()
- },
- style: {
- height: '22px',
- borderRadius: '22px',
- color: '#F9881B',
- padding: '0 20px',
- backgroundColor: '#FFE3C9',
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- fontSize: '8px',
- cursor: 'pointer',
- },
- }, [
- h('div', {
- style: {
- paddingRight: '2px',
- },
- }, 'Taking too long?'),
- h('div', {
- style: {
- textDecoration: 'underline',
- },
- }, 'Retry with a higher gas price here'),
- ]),
- ])
- )
-}
-
-TransactionListItem.prototype.resubmit = function () {
- const { transaction } = this.props
- this.props.retryTransaction(transaction.id)
-}
-
-function domainField (txParams) {
- return h('div', {
- style: {
- fontSize: 'x-small',
- color: '#ABA9AA',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- width: '100%',
- },
- }, [
- txParams.origin,
- ])
-}
-
-function recipientField (txParams, transaction, isTx, isMsg) {
- let message
-
- if (isMsg) {
- message = 'Signature Requested'
- } else if (txParams.to) {
- message = addressSummary(txParams.to)
- } else {
- message = 'Contract Deployment'
- }
-
- return h('div', {
- style: {
- fontSize: 'x-small',
- color: '#ABA9AA',
- },
- }, [
- message,
- renderErrorOrWarning(transaction),
- ])
-}
-
-function formatDate (date) {
- return vreme.format(new Date(date), 'March 16 2014 14:30')
-}
-
-function renderErrorOrWarning (transaction) {
- const { status } = transaction
-
- // show rejected
- if (status === 'rejected') {
- return h('span.error', ' (Rejected)')
- }
- if (transaction.err || transaction.warning) {
- const { err, warning = {} } = transaction
- const errFirst = !!((err && warning) || err)
-
- errFirst ? err.message : warning.message
-
- // show error
- if (err) {
- const message = err.message || ''
- return (
- h(Tooltip, {
- title: message,
- position: 'bottom',
- }, [
- h(`span.error`, ` (Failed)`),
- ])
- )
- }
-
- // show warning
- if (warning) {
- const message = warning.message
- return h(Tooltip, {
- title: message,
- position: 'bottom',
- }, [
- h(`span.warning`, ` (Warning)`),
- ])
- }
- }
-}
diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js
deleted file mode 100644
index 69b72614c..000000000
--- a/ui/app/components/transaction-list.js
+++ /dev/null
@@ -1,87 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-const TransactionListItem = require('./transaction-list-item')
-
-module.exports = TransactionList
-
-
-inherits(TransactionList, Component)
-function TransactionList () {
- Component.call(this)
-}
-
-TransactionList.prototype.render = function () {
- const { transactions, network, unapprovedMsgs, conversionRate } = this.props
-
- var shapeShiftTxList
- if (network === '1') {
- shapeShiftTxList = this.props.shapeShiftTxList
- }
- const txsToRender = !shapeShiftTxList ? transactions.concat(unapprovedMsgs) : transactions.concat(unapprovedMsgs, shapeShiftTxList)
- .sort((a, b) => b.time - a.time)
-
- return (
-
- h('section.transaction-list.full-flex-height', {
- style: {
- justifyContent: 'center',
- },
- }, [
-
- h('style', `
- .transaction-list .transaction-list-item:not(:last-of-type) {
- border-bottom: 1px solid #D4D4D4;
- }
- .transaction-list .transaction-list-item .ether-balance-label {
- display: block !important;
- font-size: small;
- }
- `),
-
- h('.tx-list', {
- style: {
- overflowY: 'auto',
- height: '100%',
- padding: '0 20px',
- textAlign: 'center',
- },
- }, [
-
- txsToRender.length
- ? txsToRender.map((transaction, i) => {
- let key
- switch (transaction.key) {
- case 'shapeshift':
- const { depositAddress, time } = transaction
- key = `shift-tx-${depositAddress}-${time}-${i}`
- break
- default:
- key = `tx-${transaction.id}-${i}`
- }
- return h(TransactionListItem, {
- transaction, i, network, key,
- conversionRate,
- showTx: (txId) => {
- this.props.viewPendingTx(txId)
- },
- })
- })
- : h('.flex-center.full-flex-height', {
- style: {
- flexDirection: 'column',
- justifyContent: 'center',
- },
- }, [
- h('p', {
- style: {
- marginTop: '50px',
- },
- }, 'No transaction history.'),
- ]),
- ]),
- ])
- )
-}
-
diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js
index 1a13070c8..d104eda88 100644
--- a/ui/app/components/tx-list-item.js
+++ b/ui/app/components/tx-list-item.js
@@ -9,18 +9,28 @@ abiDecoder.addABI(abi)
const Identicon = require('./identicon')
const contractMap = require('eth-contract-metadata')
+const actions = require('../actions')
const { conversionUtil, multiplyCurrencies } = require('../conversion-util')
const { calcTokenAmount } = require('../token-util')
const { getCurrentCurrency } = require('../selectors')
+const t = require('../../i18n')
-module.exports = connect(mapStateToProps)(TxListItem)
+module.exports = connect(mapStateToProps, mapDispatchToProps)(TxListItem)
function mapStateToProps (state) {
return {
tokens: state.metamask.tokens,
currentCurrency: getCurrentCurrency(state),
tokenExchangeRates: state.metamask.tokenExchangeRates,
+ selectedAddressTxList: state.metamask.selectedAddressTxList,
+ }
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ setSelectedToken: tokenAddress => dispatch(actions.setSelectedToken(tokenAddress)),
+ retryTransaction: transactionId => dispatch(actions.retryTransaction(transactionId)),
}
}
@@ -31,6 +41,7 @@ function TxListItem () {
this.state = {
total: null,
fiatTotal: null,
+ isTokenTx: null,
}
}
@@ -39,12 +50,13 @@ TxListItem.prototype.componentDidMount = async function () {
const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data)
const { name: txDataName } = decodedData || {}
+ const isTokenTx = txDataName === 'transfer'
- const { total, fiatTotal } = txDataName === 'transfer'
+ const { total, fiatTotal } = isTokenTx
? await this.getSendTokenTotal()
: this.getSendEtherTotal()
- this.setState({ total, fiatTotal })
+ this.setState({ total, fiatTotal, isTokenTx })
}
TxListItem.prototype.getAddressText = function () {
@@ -63,7 +75,7 @@ TxListItem.prototype.getAddressText = function () {
default:
return address
? `${address.slice(0, 10)}...${address.slice(-4)}`
- : 'Contract Deployment'
+ : t('contractDeployment')
}
}
@@ -167,22 +179,49 @@ TxListItem.prototype.getSendTokenTotal = async function () {
}
}
+TxListItem.prototype.showRetryButton = function () {
+ const {
+ transactionSubmittedTime,
+ selectedAddressTxList,
+ transactionId,
+ txParams,
+ } = this.props
+ const currentNonce = txParams.nonce
+ const currentNonceTxs = selectedAddressTxList.filter(tx => tx.txParams.nonce === currentNonce)
+ const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
+ const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[currentNonceSubmittedTxs.length - 1]
+ const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce
+ && lastSubmittedTxWithCurrentNonce.id === transactionId
+
+ return currentTxIsLatestWithNonce && Date.now() - transactionSubmittedTime > 30000
+}
+
+TxListItem.prototype.setSelectedToken = function (tokenAddress) {
+ this.props.setSelectedToken(tokenAddress)
+}
+
+TxListItem.prototype.resubmit = function () {
+ const { transactionId } = this.props
+ this.props.retryTransaction(transactionId)
+}
+
TxListItem.prototype.render = function () {
const {
transactionStatus,
transactionAmount,
onClick,
- transActionId,
+ transactionId,
dateString,
address,
className,
+ txParams,
} = this.props
- const { total, fiatTotal } = this.state
+ const { total, fiatTotal, isTokenTx } = this.state
const showFiatTotal = transactionAmount !== '0x0' && fiatTotal
return h(`div${className || ''}`, {
- key: transActionId,
- onClick: () => onClick && onClick(transActionId),
+ key: transactionId,
+ onClick: () => onClick && onClick(transactionId),
}, [
h(`div.flex-column.tx-list-item-wrapper`, {}, [
@@ -223,9 +262,10 @@ TxListItem.prototype.render = function () {
className: classnames('tx-list-status', {
'tx-list-status--rejected': transactionStatus === 'rejected',
'tx-list-status--failed': transactionStatus === 'failed',
+ 'tx-list-status--dropped': transactionStatus === 'dropped',
}),
},
- transactionStatus,
+ this.txStatusIndicator(),
),
]),
]),
@@ -240,6 +280,48 @@ TxListItem.prototype.render = function () {
]),
]),
+
+ this.showRetryButton() && h('div.tx-list-item-retry-container', [
+
+ h('span.tx-list-item-retry-copy', 'Taking too long?'),
+
+ h('span.tx-list-item-retry-link', {
+ onClick: (event) => {
+ event.stopPropagation()
+ if (isTokenTx) {
+ this.setSelectedToken(txParams.to)
+ }
+ this.resubmit()
+ },
+ }, 'Increase the gas price on your transaction'),
+
+ ]),
+
]), // holding on icon from design
])
}
+
+TxListItem.prototype.txStatusIndicator = function () {
+ const { transactionStatus } = this.props
+
+ let name
+
+ if (transactionStatus === 'unapproved') {
+ name = t('unapproved')
+ } else if (transactionStatus === 'rejected') {
+ name = t('rejected')
+ } else if (transactionStatus === 'approved') {
+ name = t('approved')
+ } else if (transactionStatus === 'signed') {
+ name = t('signed')
+ } else if (transactionStatus === 'submitted') {
+ name = t('submitted')
+ } else if (transactionStatus === 'confirmed') {
+ name = t('confirmed')
+ } else if (transactionStatus === 'failed') {
+ name = t('failed')
+ } else if (transactionStatus === 'dropped') {
+ name = t('dropped')
+ }
+ return name
+}
diff --git a/ui/app/components/tx-list.js b/ui/app/components/tx-list.js
index 1729e6a6f..037c7de8c 100644
--- a/ui/app/components/tx-list.js
+++ b/ui/app/components/tx-list.js
@@ -10,6 +10,7 @@ const { formatDate } = require('../util')
const { showConfTxPage } = require('../actions')
const classnames = require('classnames')
const { tokenInfoGetter } = require('../token-util')
+const t = require('../../i18n')
module.exports = connect(mapStateToProps, mapDispatchToProps)(TxList)
@@ -39,7 +40,7 @@ TxList.prototype.render = function () {
return h('div.flex-column', [
h('div.flex-row.tx-list-header-wrapper', [
h('div.flex-row.tx-list-header', [
- h('div', 'transactions'),
+ h('div', t('transactions')),
]),
]),
h('div.flex-column.tx-list-container', {}, [
@@ -56,7 +57,7 @@ TxList.prototype.renderTransaction = function () {
: [h(
'div.tx-list-item.tx-list-item--empty',
{ key: 'tx-list-none' },
- [ 'No Transactions' ],
+ [ t('noTransactions') ],
)]
}
@@ -74,9 +75,10 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
address: transaction.txParams.to,
transactionStatus: transaction.status,
transactionAmount: transaction.txParams.value,
- transActionId: transaction.id,
+ transactionId: transaction.id,
transactionHash: transaction.hash,
transactionNetworkId: transaction.metamaskNetworkId,
+ transactionSubmittedTime: transaction.submittedTime,
}
const {
@@ -84,30 +86,32 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
transactionStatus,
transactionAmount,
dateString,
- transActionId,
+ transactionId,
transactionHash,
transactionNetworkId,
+ transactionSubmittedTime,
} = props
const { showConfTxPage } = this.props
const opts = {
- key: transActionId || transactionHash,
+ key: transactionId || transactionHash,
txParams: transaction.txParams,
transactionStatus,
- transActionId,
+ transactionId,
dateString,
address,
transactionAmount,
transactionHash,
conversionRate,
tokenInfoGetter: this.tokenInfoGetter,
+ transactionSubmittedTime,
}
const isUnapproved = transactionStatus === 'unapproved'
if (isUnapproved) {
- opts.onClick = () => showConfTxPage({id: transActionId})
- opts.transactionStatus = 'Not Started'
+ opts.onClick = () => showConfTxPage({id: transactionId})
+ opts.transactionStatus = t('Not Started')
} else if (transactionHash) {
opts.onClick = () => this.view(transactionHash, transactionNetworkId)
}
diff --git a/ui/app/components/tx-view.js b/ui/app/components/tx-view.js
index b25d8e0f9..96d776270 100644
--- a/ui/app/components/tx-view.js
+++ b/ui/app/components/tx-view.js
@@ -5,6 +5,7 @@ const ethUtil = require('ethereumjs-util')
const inherits = require('util').inherits
const actions = require('../actions')
const selectors = require('../selectors')
+const t = require('../../i18n')
const BalanceComponent = require('./balance-component')
const TxList = require('./tx-list')
@@ -68,25 +69,25 @@ TxView.prototype.renderButtons = function () {
return !selectedToken
? (
h('div.flex-row.flex-center.hero-balance-buttons', [
- h('button.btn-clear.hero-balance-button', {
+ h('button.btn-clear.hero-balance-button.allcaps', {
onClick: () => showModal({
name: 'DEPOSIT_ETHER',
}),
- }, 'DEPOSIT'),
+ }, t('deposit')),
- h('button.btn-clear.hero-balance-button', {
+ h('button.btn-clear.hero-balance-button.allcaps', {
style: {
marginLeft: '0.8em',
},
onClick: showSendPage,
- }, 'SEND'),
+ }, t('send')),
])
)
: (
h('div.flex-row.flex-center.hero-balance-buttons', [
h('button.btn-clear.hero-balance-button', {
onClick: showSendTokenPage,
- }, 'SEND'),
+ }, t('send')),
])
)
}
@@ -100,9 +101,10 @@ TxView.prototype.render = function () {
h('div.flex-row.phone-visible', {
style: {
- margin: '1.5em 1.2em 0',
justifyContent: 'space-between',
alignItems: 'center',
+ flex: '0 0 auto',
+ margin: '10px',
},
}, [
@@ -110,11 +112,10 @@ TxView.prototype.render = function () {
style: {
fontSize: '1.3em',
cursor: 'pointer',
+ padding: '10px',
},
- onClick: () => {
- this.props.sidebarOpen ? this.props.hideSidebar() : this.props.showSidebar()
- },
- }, []),
+ onClick: () => this.props.sidebarOpen ? this.props.hideSidebar() : this.props.showSidebar(),
+ }),
h('.identicon-wrapper.select-none', {
style: {
diff --git a/ui/app/components/typed-message-renderer.js b/ui/app/components/typed-message-renderer.js
deleted file mode 100644
index d170d63b7..000000000
--- a/ui/app/components/typed-message-renderer.js
+++ /dev/null
@@ -1,42 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const extend = require('xtend')
-
-module.exports = TypedMessageRenderer
-
-inherits(TypedMessageRenderer, Component)
-function TypedMessageRenderer () {
- Component.call(this)
-}
-
-TypedMessageRenderer.prototype.render = function () {
- const props = this.props
- const { value, style } = props
- const text = renderTypedData(value)
-
- const defaultStyle = extend({
- width: '315px',
- maxHeight: '210px',
- resize: 'none',
- border: 'none',
- background: 'white',
- padding: '3px',
- overflow: 'scroll',
- }, style)
-
- return (
- h('div.font-small', {
- style: defaultStyle,
- }, text)
- )
-}
-
-function renderTypedData (values) {
- return values.map(function (value) {
- return h('div', {}, [
- h('strong', {style: {display: 'block', fontWeight: 'bold'}}, String(value.name) + ':'),
- h('div', {}, value.value),
- ])
- })
-}
diff --git a/ui/app/components/wallet-content-display.js b/ui/app/components/wallet-content-display.js
deleted file mode 100644
index bfa061be4..000000000
--- a/ui/app/components/wallet-content-display.js
+++ /dev/null
@@ -1,56 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-module.exports = WalletContentDisplay
-
-inherits(WalletContentDisplay, Component)
-function WalletContentDisplay () {
- Component.call(this)
-}
-
-WalletContentDisplay.prototype.render = function () {
- const { title, amount, fiatValue, active, style } = this.props
-
- // TODO: Separate component: wallet-content-account
- return h('div.flex-column', {
- style: {
- marginLeft: '1.3em',
- alignItems: 'flex-start',
- ...style,
- },
- }, [
-
- h('span', {
- style: {
- fontSize: '1.1em',
- },
- }, title),
-
- h('span', {
- style: {
- fontSize: '1.8em',
- margin: '0.4em 0em',
- },
- }, amount),
-
- h('span', {
- style: {
- fontSize: '1.3em',
- },
- }, fiatValue),
-
- active && h('div', {
- style: {
- position: 'absolute',
- marginLeft: '-1.3em',
- height: '6em',
- width: '0.3em',
- background: '#D8D8D8', // $alto
- },
- }, [
- ]),
- ])
-
-}
-
diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js
index 34f27ca2a..18452205c 100644
--- a/ui/app/components/wallet-view.js
+++ b/ui/app/components/wallet-view.js
@@ -11,6 +11,7 @@ const actions = require('../actions')
const BalanceComponent = require('./balance-component')
const TokenList = require('./token-list')
const selectors = require('../selectors')
+const t = require('../../i18n')
module.exports = connect(mapStateToProps, mapDispatchToProps)(WalletView)
@@ -116,7 +117,7 @@ WalletView.prototype.render = function () {
onClick: hideSidebar,
}),
- h('div.wallet-view__keyring-label', isLoose ? 'IMPORTED' : ''),
+ h('div.wallet-view__keyring-label.allcaps', isLoose ? t('imported') : ''),
h('div.flex-column.flex-center.wallet-view__name-container', {
style: { margin: '0 auto' },
@@ -133,13 +134,13 @@ WalletView.prototype.render = function () {
selectedIdentity.name,
]),
- h('button.btn-clear.wallet-view__details-button', 'DETAILS'),
+ h('button.btn-clear.wallet-view__details-button.allcaps', t('details')),
]),
]),
h(Tooltip, {
position: 'bottom',
- title: this.state.hasCopied ? 'Copied!' : 'Copy to clipboard',
+ title: this.state.hasCopied ? t('copiedExclamation') : t('copyToClipboard'),
wrapperClassName: 'wallet-view__tooltip',
}, [
h('button.wallet-view__address', {
@@ -172,7 +173,7 @@ WalletView.prototype.render = function () {
showAddTokenPage()
hideSidebar()
},
- }, 'Add Token'),
+ }, t('addToken')),
])
}
diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js
index b4ffc48b7..1070436c3 100644
--- a/ui/app/conf-tx.js
+++ b/ui/app/conf-tx.js
@@ -40,6 +40,7 @@ function mapStateToProps (state) {
currentCurrency: state.metamask.currentCurrency,
blockGasLimit: state.metamask.currentBlockGasLimit,
computedBalances: state.metamask.computedBalances,
+ selectedAddressTxList: state.metamask.selectedAddressTxList,
}
}
@@ -48,6 +49,23 @@ function ConfirmTxScreen () {
Component.call(this)
}
+ConfirmTxScreen.prototype.componentDidUpdate = function (prevProps) {
+ const {
+ unapprovedTxs,
+ network,
+ selectedAddressTxList,
+ } = this.props
+ const { index: prevIndex, unapprovedTxs: prevUnapprovedTxs } = prevProps
+ const prevUnconfTxList = txHelper(prevUnapprovedTxs, {}, {}, {}, network)
+ const prevTxData = prevUnconfTxList[prevIndex] || {}
+ const prevTx = selectedAddressTxList.find(({ id }) => id === prevTxData.id) || {}
+ const unconfTxList = txHelper(unapprovedTxs, {}, {}, {}, network)
+
+ if (prevTx.status === 'dropped' && unconfTxList.length === 0) {
+ this.goHome({})
+ }
+}
+
ConfirmTxScreen.prototype.render = function () {
const props = this.props
const {
diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js
index ee42ebea1..d484ed16d 100644
--- a/ui/app/conversion-util.js
+++ b/ui/app/conversion-util.js
@@ -187,6 +187,18 @@ const conversionGreaterThan = (
return firstValue.gt(secondValue)
}
+const conversionMax = (
+ { ...firstProps },
+ { ...secondProps },
+) => {
+ const firstIsGreater = conversionGreaterThan(
+ { ...firstProps },
+ { ...secondProps }
+ )
+
+ return firstIsGreater ? firstProps.value : secondProps.value
+}
+
const conversionGTE = (
{ ...firstProps },
{ ...secondProps },
@@ -216,6 +228,7 @@ module.exports = {
conversionGreaterThan,
conversionGTE,
conversionLTE,
+ conversionMax,
toNegative,
subtractCurrencies,
}
diff --git a/ui/app/css/itcss/components/account-menu.scss b/ui/app/css/itcss/components/account-menu.scss
index 4752741aa..c4037d862 100644
--- a/ui/app/css/itcss/components/account-menu.scss
+++ b/ui/app/css/itcss/components/account-menu.scss
@@ -87,7 +87,6 @@
flex: 1 0 auto;
display: flex;
flex-flow: column nowrap;
- padding-top: 4px;
}
&__check-mark {
@@ -115,13 +114,11 @@
color: $white;
font-size: 18px;
font-weight: 300;
- line-height: 16px;
}
&__balance {
color: $dusty-gray;
font-size: 14px;
- line-height: 19px;
}
&__action {
diff --git a/ui/app/css/itcss/components/buttons.scss b/ui/app/css/itcss/components/buttons.scss
index 1450b71cc..8df8829f2 100644
--- a/ui/app/css/itcss/components/buttons.scss
+++ b/ui/app/css/itcss/components/buttons.scss
@@ -45,6 +45,18 @@
}
}
+.btn-confirm {
+ background-color: $caribbean-green; // TODO: reusable color in colors.css
+ text-align: center;
+ padding: .8rem 1rem;
+ color: $white;
+ border: 2px solid $caribbean-green;
+ border-radius: 4px;
+ font-size: .85rem;
+ font-weight: 400;
+ transition: border-color .3s ease;
+}
+
// No longer used in flat design, remove when modal buttons done
// div.wallet-btn {
// border: 1px solid rgb(91, 93, 103);
diff --git a/ui/app/css/itcss/components/confirm.scss b/ui/app/css/itcss/components/confirm.scss
index 878495290..abe138f54 100644
--- a/ui/app/css/itcss/components/confirm.scss
+++ b/ui/app/css/itcss/components/confirm.scss
@@ -202,7 +202,7 @@
}
.confirm-screen-label {
- font-size: 18px;
+ font-size: 16px;
line-height: 40px;
color: $scorpion;
text-align: left;
@@ -229,7 +229,6 @@ section .confirm-screen-account-number,
.confirm-screen-row {
display: flex;
flex-flow: row nowrap;
- border-bottom: 1px solid $alto;
width: 100%;
align-items: center;
padding: 12px;
@@ -237,6 +236,26 @@ section .confirm-screen-account-number,
font-size: 16px;
line-height: 22px;
font-weight: 300;
+
+ &:not(:last-of-type) {
+ border-bottom: 1px solid $alto;
+ }
+}
+
+@media screen and (max-width: 379px) {
+ .confirm-screen-row {
+ span.confirm-screen-section-column {
+ flex: 0.4;
+ }
+
+ div.confirm-screen-section-column {
+ flex: 0.6;
+ }
+
+ .currency-display__input {
+ font-size: 14px;
+ }
+ }
}
.confirm-screen-row-detail {
@@ -247,12 +266,9 @@ section .confirm-screen-account-number,
.confirm-screen-total-box {
background-color: $wild-sand;
- padding: 20px;
- padding-left: 35px;
- border-bottom: 1px solid $alto;
.confirm-screen-label {
- line-height: 18px;
+ line-height: 21px;
}
.confirm-screen-row-detail {
@@ -261,7 +277,7 @@ section .confirm-screen-account-number,
&__subtitle {
font-size: 12px;
- line-height: 22px;
+ line-height: 16px;
}
.confirm-screen-row-info {
@@ -304,21 +320,3 @@ section .confirm-screen-account-number,
font-weight: 300;
margin: 0 5px;
}
-
-#pending-tx-form {
- flex: 1 0 auto;
- position: relative;
- display: flex;
- flex-flow: row nowrap;
- background-color: $white;
- padding: 12px;
- border-bottom-left-radius: 8px;
- border-bottom-right-radius: 8px;
- width: 100%;
-
- @media screen and (max-width: $break-small) {
- border-top: 1px solid $alto;
- border-bottom-left-radius: 0;
- border-bottom-right-radius: 0;
- }
-}
diff --git a/ui/app/css/itcss/components/hero-balance.scss b/ui/app/css/itcss/components/hero-balance.scss
index 4af0c2c55..a3f051361 100644
--- a/ui/app/css/itcss/components/hero-balance.scss
+++ b/ui/app/css/itcss/components/hero-balance.scss
@@ -5,9 +5,6 @@
flex-direction: column;
justify-content: flex-start;
align-items: center;
- margin: .3em .9em 0;
- // height: 80vh;
- // max-height: 225px;
flex: 0 0 auto;
}
diff --git a/ui/app/css/itcss/components/index.scss b/ui/app/css/itcss/components/index.scss
index 0219f9fb2..ffd43ecbf 100644
--- a/ui/app/css/itcss/components/index.scss
+++ b/ui/app/css/itcss/components/index.scss
@@ -55,3 +55,7 @@
@import './new-account.scss';
@import './tooltip.scss';
+
+@import './welcome-screen.scss';
+
+@import './sender-to-recipient.scss';
diff --git a/ui/app/css/itcss/components/modal.scss b/ui/app/css/itcss/components/modal.scss
index 53e3bff00..a8d5e8dc2 100644
--- a/ui/app/css/itcss/components/modal.scss
+++ b/ui/app/css/itcss/components/modal.scss
@@ -641,32 +641,40 @@
&__buy-rows {
width: 100%;
- padding: 33px;
- padding-top: 0px;
+ padding: 0 30px;
display: flex;
flex-flow: column nowrap;
flex: 1;
- overflow-y: auto;
@media screen and (max-width: 575px) {
height: 0;
}
}
+ &__logo {
+ height: 60px;
+ background-repeat: no-repeat;
+ background-size: contain;
+ background-position: center;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
&__buy-row {
border-bottom: 1px solid $alto;
display: flex;
justify-content: space-between;
align-items: center;
- flex: 1;
- padding-bottom: 25px;
- padding-top: 25px;
+ flex: 1 0 auto;
+ padding: 30px 0 20px;
+ min-height: 170px;
@media screen and (max-width: 575px) {
- min-height: 360px;
+ min-height: 270px;
flex-flow: column;
- justify-content: center;
- padding-top: 45px;
+ justify-content: flex-start;
}
&__back {
@@ -692,34 +700,29 @@
}
}
- &__logo {
+ &__logo-container {
display: flex;
justify-content: center;
- flex: 0.3 1 auto;
+ flex: 0 0 auto;
+ padding: 0 20px;
- @media screen and (min-width: 575px) {
- min-width: 215px;
+ @media screen and (min-width: 576px) {
+ width: 12rem;
+ }
+
+ @media screen and (max-width: 575px) {
+ width: 100%;
+ max-height: 6rem;
+ padding-bottom: 20px;
}
}
&__coinbase-logo {
height: 40px;
- width: 180px;
}
&__shapeshift-logo {
height: 60px;
- width: 174px;
- }
-
- &__eth-logo {
- border-radius: 50%;
- width: 68px;
- height: 68px;
- border: 3px solid $tundora;
- z-index: 25;
- padding: 4px;
- background-color: #fff;
}
&__right {
@@ -728,10 +731,11 @@
&__description {
color: $cape-cod;
- flex: 0.5 1 auto;
+ padding-bottom: 20px;
+ align-self: flex-start;
@media screen and (min-width: 575px) {
- min-width: 315px;
+ width: 15rem;
}
&__title {
diff --git a/ui/app/css/itcss/components/network.scss b/ui/app/css/itcss/components/network.scss
index c32d1de5e..374cb71b6 100644
--- a/ui/app/css/itcss/components/network.scss
+++ b/ui/app/css/itcss/components/network.scss
@@ -10,8 +10,9 @@
.network-component.pointer {
border: 2px solid $silver;
border-radius: 82px;
- padding: 3px;
+ padding: 7px 3px;
flex: 0 0 auto;
+ display: flex;
&.ethereum-network .menu-icon-circle div {
background-color: rgba(3, 135, 137, .7) !important;
diff --git a/ui/app/css/itcss/components/new-account.scss b/ui/app/css/itcss/components/new-account.scss
index 08cff867d..c2cefe4ad 100644
--- a/ui/app/css/itcss/components/new-account.scss
+++ b/ui/app/css/itcss/components/new-account.scss
@@ -161,15 +161,14 @@
display: flex;
flex-flow: column;
align-items: center;
+ padding: 30px 30px 0;
&__input-label {
color: $scorpion;
font-family: Roboto;
font-size: 16px;
line-height: 21px;
- margin-top: 29px;
align-self: flex-start;
- margin-left: 30px;
}
&__input {
diff --git a/ui/app/css/itcss/components/newui-sections.scss b/ui/app/css/itcss/components/newui-sections.scss
index ecf5e1036..777a82318 100644
--- a/ui/app/css/itcss/components/newui-sections.scss
+++ b/ui/app/css/itcss/components/newui-sections.scss
@@ -51,6 +51,7 @@ $wallet-view-bg: $alabaster;
cursor: pointer;
display: flex;
justify-content: center;
+ padding: 10px;
}
// wallet view and sidebar
@@ -264,7 +265,6 @@ $wallet-view-bg: $alabaster;
.account-name {
font-size: 24px;
font-weight: 300;
- line-height: 20px;
color: $black;
margin-top: 8px;
margin-bottom: .9rem;
@@ -291,7 +291,6 @@ $wallet-view-bg: $alabaster;
padding-right: 6px;
}
-
// first time
.first-view-main {
display: flex;
@@ -313,4 +312,23 @@ $wallet-view-bg: $alabaster;
@media screen and (min-width: 1281px) {
width: 62vw;
}
-} \ No newline at end of file
+}
+
+.unlock-screen-container {
+ z-index: $main-container-z-index;
+ font-family: Roboto;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex: 1 0 auto;
+ background: #f7f7f7;
+ width: 100%;
+}
+
+.unlock-screen {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ flex: 1 0 auto;
+}
diff --git a/ui/app/css/itcss/components/send.scss b/ui/app/css/itcss/components/send.scss
index bb17e53cd..bdea1b008 100644
--- a/ui/app/css/itcss/components/send.scss
+++ b/ui/app/css/itcss/components/send.scss
@@ -660,6 +660,13 @@
&__gas-fee-display {
width: 100%;
+ position: relative;
+
+ .currency-display--message {
+ padding: 8px 38px 8px 10px;
+ display: flex;
+ align-items: center;
+ }
}
&__sliders-icon-container {
@@ -885,3 +892,23 @@
}
}
}
+
+.sliders-icon-container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 24px;
+ width: 24px;
+ border: 1px solid $curious-blue;
+ border-radius: 4px;
+ background-color: $white;
+ position: absolute;
+ right: 15px;
+ top: 14px;
+ cursor: pointer;
+ font-size: 1em;
+}
+
+.sliders-icon {
+ color: $curious-blue;
+} \ No newline at end of file
diff --git a/ui/app/css/itcss/components/sender-to-recipient.scss b/ui/app/css/itcss/components/sender-to-recipient.scss
new file mode 100644
index 000000000..f16013cdf
--- /dev/null
+++ b/ui/app/css/itcss/components/sender-to-recipient.scss
@@ -0,0 +1,58 @@
+.sender-to-recipient {
+ &__container {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ border-bottom: 1px solid $geyser;
+ position: relative;
+ }
+
+ &__sender,
+ &__recipient {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ flex: 1;
+ padding: 10px 20px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ &__sender {
+ padding-right: 30px;
+ }
+
+ &__recipient {
+ border-left: 1px solid $geyser;
+ padding-left: 30px;
+ }
+
+ &__arrow-container {
+ position: absolute;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ &__arrow-circle {
+ background: $white;
+ padding: 5px;
+ border: 1px solid $geyser;
+ border-radius: 20px;
+ height: 30px;
+ width: 30px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ &__name {
+ padding-left: 5px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+}
diff --git a/ui/app/css/itcss/components/transaction-list.scss b/ui/app/css/itcss/components/transaction-list.scss
index c3df493df..d03faf486 100644
--- a/ui/app/css/itcss/components/transaction-list.scss
+++ b/ui/app/css/itcss/components/transaction-list.scss
@@ -97,7 +97,7 @@
.tx-list-content-wrapper {
align-items: stretch;
- margin-bottom: 4px;
+ margin: 4px 0;
flex: 1 0 auto;
width: 100%;
display: flex;
@@ -126,6 +126,54 @@
}
}
+.tx-list-item-retry-container {
+ background: #d1edff;
+ width: 100%;
+ border-radius: 4px;
+ font-size: 0.8em;
+ display: flex;
+ justify-content: center;
+ margin-left: 44px;
+ width: calc(100% - 44px);
+
+ @media screen and (min-width: 576px) and (max-width: 679px) {
+ flex-flow: column;
+ align-items: center;
+ }
+
+ @media screen and (min-width: 380px) and (max-width: 575px) {
+ flex-flow: row;
+ }
+
+ @media screen and (max-width: 379px) {
+ flex-flow: column;
+ align-items: center;
+ }
+}
+
+.tx-list-item-retry-copy {
+ font-family: Roboto;
+}
+
+.tx-list-item-retry-link {
+ text-decoration: underline;
+ margin-left: 6px;
+ cursor: pointer;
+
+ @media screen and (min-width: 576px) and (max-width: 679px) {
+ margin-left: 0px;
+ }
+
+ @media screen and (min-width: 380px) and (max-width: 575px) {
+ margin-left: 6px;
+ }
+
+ @media screen and (max-width: 379px) {
+ margin-left: 0px;
+ text-align: center;
+ }
+}
+
.tx-list-date {
color: $dusty-gray;
font-size: 12px;
@@ -136,6 +184,7 @@
align-self: center;
flex: 0 0 auto;
margin-right: 16px;
+ display: flex;
}
.tx-list-account-and-status-wrapper {
@@ -189,6 +238,10 @@
.tx-list-status--failed {
color: $monzo;
}
+
+ .tx-list-status--dropped {
+ opacity: 0.5;
+ }
}
.tx-list-item {
diff --git a/ui/app/css/itcss/components/welcome-screen.scss b/ui/app/css/itcss/components/welcome-screen.scss
new file mode 100644
index 000000000..bfd174ad9
--- /dev/null
+++ b/ui/app/css/itcss/components/welcome-screen.scss
@@ -0,0 +1,59 @@
+.welcome-screen {
+ display: flex;
+ flex-flow: column;
+ justify-content: center;
+ align-items: center;
+ font-family: Roboto;
+ font-weight: 400;
+ width: 100%;
+ flex: 1 0 auto;
+ padding: 70px 0;
+ background: $white;
+
+ @media screen and (max-width: 575px) {
+ padding: 0;
+ }
+
+ &__info {
+ display: flex;
+ flex-flow: column;
+ width: 100%;
+ height: 100%;
+ align-items: center;
+
+ &__header {
+ font-size: 1.65em;
+ margin-bottom: 14px;
+
+ @media screen and (max-width: 575px) {
+ font-size: 1.5em;
+ }
+ }
+
+ &__copy {
+ font-size: 1em;
+ width: 400px;
+ max-width: 90vw;
+ text-align: center;
+
+ @media screen and (max-width: 575px) {
+ font-size: 0.9em;
+ }
+ }
+ }
+
+ &__button {
+ height: 54px;
+ width: 198px;
+ box-shadow: 0 2px 4px 0 rgba(0,0,0,0.14);
+ color: #FFFFFF;
+ font-size: 20px;
+ font-weight: 500;
+ line-height: 26px;
+ text-align: center;
+ text-transform: uppercase;
+ margin: 35px 0 14px;
+ transition: 200ms ease-in-out;
+ background-color: rgba(247, 134, 28, 0.9);
+ }
+}
diff --git a/ui/app/css/itcss/generic/index.scss b/ui/app/css/itcss/generic/index.scss
index 9b3d7475b..1e226b93e 100644
--- a/ui/app/css/itcss/generic/index.scss
+++ b/ui/app/css/itcss/generic/index.scss
@@ -13,7 +13,6 @@ body {
font-family: Roboto, Arial;
color: #4d4d4d;
font-weight: 300;
- line-height: 1.4em;
background: #f7f7f7;
width: 100%;
height: 100%;
@@ -70,6 +69,10 @@ input.large-input {
height: 36px;
}
+.allcaps {
+ text-transform: uppercase;
+}
+
.page-container {
width: 400px;
background-color: $white;
@@ -77,25 +80,30 @@ input.large-input {
z-index: 25;
display: flex;
flex-flow: column;
+ border-radius: 7px;
&__header {
display: flex;
flex-flow: column;
border-bottom: 1px solid $geyser;
- padding: 1.15rem 0.95rem;
+ padding: 20px;
flex: 0 0 auto;
- background: $alabaster;
position: relative;
}
- &__header-close::after {
- content: '\00D7';
- font-size: 40px;
+ &__header-close {
color: $tundora;
position: absolute;
- top: 21.5px;
- right: 28.5px;
+ top: 20px;
+ right: 20px;
cursor: pointer;
+ overflow: hidden;
+
+ &::after {
+ content: '\00D7';
+ font-size: 40px;
+ line-height: 20px;
+ }
}
&__footer {
@@ -107,14 +115,15 @@ input.large-input {
flex: 0 0 auto;
.btn-clear,
- .btn-cancel {
+ .btn-cancel,
+ .btn-confirm {
font-size: 1rem;
}
}
&__footer-button {
width: 165px;
- height: 60px;
+ height: 55px;
font-size: 1rem;
text-transform: uppercase;
margin-right: 1rem;
@@ -125,12 +134,19 @@ input.large-input {
}
}
+ &__back-button {
+ color: #2f9ae0;
+ font-size: 1rem;
+ cursor: pointer;
+ padding-bottom: 10px;
+ font-weight: 400;
+ }
+
&__title {
color: $black;
- font-family: Roboto;
font-size: 2rem;
font-weight: 500;
- line-height: initial;
+ line-height: 2rem;
}
&__subtitle {
@@ -174,6 +190,19 @@ input.large-input {
}
}
}
+
+ &--full-width {
+ width: initial;
+ }
+
+ &--full-height {
+ height: 100%;
+ }
+
+ &__content {
+ height: 100%;
+ overflow-y: auto;
+ }
}
@media screen and (max-width: 250px) {
@@ -200,5 +229,6 @@ input.large-input {
width: 100%;
overflow-y: auto;
background-color: $white;
+ border-radius: 0;
}
}
diff --git a/ui/app/css/itcss/generic/reset.scss b/ui/app/css/itcss/generic/reset.scss
index e054d533e..a417a0453 100644
--- a/ui/app/css/itcss/generic/reset.scss
+++ b/ui/app/css/itcss/generic/reset.scss
@@ -112,10 +112,6 @@ section {
display: block;
}
-body {
- line-height: 1;
-}
-
ol,
ul {
list-style: none;
diff --git a/ui/app/css/itcss/settings/variables.scss b/ui/app/css/itcss/settings/variables.scss
index d96c1ae43..640fd95b8 100644
--- a/ui/app/css/itcss/settings/variables.scss
+++ b/ui/app/css/itcss/settings/variables.scss
@@ -46,6 +46,7 @@ $manatee: #93949d;
$spindle: #c7ddec;
$mid-gray: #5b5d67;
$cape-cod: #38393a;
+$onahau: #d1edff;
$java: #29b6af;
$wild-strawberry: #ff4a8d;
$cornflower-blue: #7057ff;
diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js
index 0e08da8db..370fdd5b7 100644
--- a/ui/app/first-time/init-menu.js
+++ b/ui/app/first-time/init-menu.js
@@ -6,6 +6,7 @@ const h = require('react-hyperscript')
const Mascot = require('../components/mascot')
const actions = require('../actions')
const Tooltip = require('../components/tooltip')
+const t = require('../../i18n')
const getCaretCoordinates = require('textarea-caret')
const environmentType = require('../../../app/scripts/lib/environment-type')
const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/config').enums
@@ -59,7 +60,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) {
color: '#7F8082',
marginBottom: 10,
},
- }, 'MetaMask'),
+ }, t('appName')),
h('div', [
@@ -69,10 +70,10 @@ InitializeMenuScreen.prototype.renderMenu = function (state) {
color: '#7F8082',
display: 'inline',
},
- }, 'Encrypt your new DEN'),
+ }, t('encryptNewDen')),
h(Tooltip, {
- title: 'Your DEN is your password-encrypted storage within MetaMask.',
+ title: t('denExplainer'),
}, [
h('i.fa.fa-question-circle.pointer', {
style: {
@@ -92,7 +93,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) {
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box',
- placeholder: 'New Password (min 8 chars)',
+ placeholder: t('newPassword'),
onInput: this.inputChanged.bind(this),
style: {
width: 260,
@@ -104,7 +105,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) {
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box-confirm',
- placeholder: 'Confirm Password',
+ placeholder: t('confirmPassword'),
onKeyPress: this.createVaultOnEnter.bind(this),
onInput: this.inputChanged.bind(this),
style: {
@@ -119,7 +120,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) {
style: {
margin: 12,
},
- }, 'Create'),
+ }, t('createDen')),
h('.flex-row.flex-center.flex-grow', [
h('p.pointer', {
@@ -129,7 +130,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) {
color: 'rgb(247, 134, 28)',
textDecoration: 'underline',
},
- }, 'Import Existing DEN'),
+ }, t('importDen')),
]),
h('.flex-row.flex-center.flex-grow', [
@@ -178,12 +179,12 @@ InitializeMenuScreen.prototype.createNewVaultAndKeychain = function () {
var passwordConfirm = passwordConfirmBox.value
if (password.length < 8) {
- this.warning = 'password not long enough'
+ this.warning = t('passwordShort')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
if (password !== passwordConfirm) {
- this.warning = 'passwords don\'t match'
+ this.warning = t('passwordMismatch')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
diff --git a/ui/app/info.js b/ui/app/info.js
deleted file mode 100644
index 49ff9f24a..000000000
--- a/ui/app/info.js
+++ /dev/null
@@ -1,154 +0,0 @@
-const inherits = require('util').inherits
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-const actions = require('./actions')
-
-module.exports = connect(mapStateToProps)(InfoScreen)
-
-function mapStateToProps (state) {
- return {}
-}
-
-inherits(InfoScreen, Component)
-function InfoScreen () {
- Component.call(this)
-}
-
-InfoScreen.prototype.render = function () {
- const state = this.props
- const version = global.platform.getVersion()
-
- return (
- h('.flex-column.flex-grow', {
- style: {
- maxWidth: '400px',
- },
- }, [
-
- // subtitle and nav
- h('.section-title.flex-row.flex-center', [
- h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
- onClick: (event) => {
- state.dispatch(actions.goHome())
- },
- }),
- h('h2.page-subtitle', 'Info'),
- ]),
-
- // main view
- h('.flex-column.flex-justify-center.flex-grow.select-none', [
- h('.flex-space-around', {
- style: {
- padding: '20px',
- },
- }, [
- // current version number
-
- h('.info.info-gray', [
- h('div', 'Metamask'),
- h('div', {
- style: {
- marginBottom: '10px',
- },
- }, `Version: ${version}`),
- ]),
-
- h('div', {
- style: {
- marginBottom: '5px',
- }},
- [
- h('div', [
- h('a', {
- href: 'https://metamask.io/privacy.html',
- target: '_blank',
- onClick (event) { this.navigateTo(event.target.href) },
- }, [
- h('div.info', 'Privacy Policy'),
- ]),
- ]),
- h('div', [
- h('a', {
- href: 'https://metamask.io/terms.html',
- target: '_blank',
- onClick (event) { this.navigateTo(event.target.href) },
- }, [
- h('div.info', 'Terms of Use'),
- ]),
- ]),
- h('div', [
- h('a', {
- href: 'https://metamask.io/attributions.html',
- target: '_blank',
- onClick (event) { this.navigateTo(event.target.href) },
- }, [
- h('div.info', 'Attributions'),
- ]),
- ]),
- ]
- ),
-
- h('hr', {
- style: {
- margin: '10px 0 ',
- width: '7em',
- },
- }),
-
- h('div', {
- style: {
- paddingLeft: '30px',
- }},
- [
- h('div.fa.fa-support', [
- h('a.info', {
- href: 'https://metamask.helpscoutdocs.com/',
- target: '_blank',
- }, 'Visit our Knowledge Base'),
- ]),
-
- h('div', [
- h('a', {
- href: 'https://metamask.io/',
- target: '_blank',
- }, [
- h('img.icon-size', {
- src: 'images/icon-128.png',
- style: {
- // IE6-9
- filter: 'grayscale(100%)',
- // Microsoft Edge and Firefox 35+
- WebkitFilter: 'grayscale(100%)',
- },
- }),
- h('div.info', 'Visit our web site'),
- ]),
- ]),
-
- h('div', [
- h('.fa.fa-twitter', [
- h('a.info', {
- href: 'https://twitter.com/metamask_io',
- target: '_blank',
- }, 'Follow us on Twitter'),
- ]),
- ]),
-
- h('div.fa.fa-envelope', [
- h('a.info', {
- target: '_blank',
- href: 'mailto:support@metamask.io?subject=MetaMask Support',
- }, 'Email us!'),
- ]),
- ]),
- ]),
- ]),
- ])
- )
-}
-
-InfoScreen.prototype.navigateTo = function (url) {
- global.platform.openWindow({ url })
-}
-
diff --git a/ui/app/keychains/hd/recover-seed/confirmation.js b/ui/app/keychains/hd/recover-seed/confirmation.js
index 4335186a5..bc5339549 100644
--- a/ui/app/keychains/hd/recover-seed/confirmation.js
+++ b/ui/app/keychains/hd/recover-seed/confirmation.js
@@ -4,6 +4,7 @@ const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const actions = require('../../../actions')
+const t = require('../../../../i18n')
module.exports = connect(mapStateToProps)(RevealSeedConfirmation)
@@ -49,13 +50,13 @@ RevealSeedConfirmation.prototype.render = function () {
},
}, [
- h('h4', 'Do not recover your seed words in a public place! These words can be used to steal all your accounts.'),
+ h('h4', t('revealSeedWordsWarning')),
// confirmation
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box',
- placeholder: 'Enter your password to confirm',
+ placeholder: t('enterPasswordConfirm'),
onKeyPress: this.checkConfirmation.bind(this),
style: {
width: 260,
@@ -91,7 +92,7 @@ RevealSeedConfirmation.prototype.render = function () {
),
props.inProgress && (
- h('span.in-progress-notification', 'Generating Seed...')
+ h('span.in-progress-notification', t('generatingSeed'))
),
]),
])
diff --git a/ui/app/keychains/hd/restore-vault.js b/ui/app/keychains/hd/restore-vault.js
index d1761f17d..5e4e004cf 100644
--- a/ui/app/keychains/hd/restore-vault.js
+++ b/ui/app/keychains/hd/restore-vault.js
@@ -2,6 +2,7 @@ const inherits = require('util').inherits
const PersistentForm = require('../../../lib/persistent-form')
const connect = require('react-redux').connect
const h = require('react-hyperscript')
+const t = require('../../../i18n')
const actions = require('../../actions')
module.exports = connect(mapStateToProps)(RestoreVaultScreen)
@@ -36,23 +37,23 @@ RestoreVaultScreen.prototype.render = function () {
padding: 6,
},
}, [
- 'Restore Vault',
+ t('restoreVault'),
]),
// wallet seed entry
- h('h3', 'Wallet Seed'),
+ h('h3', t('walletSeed')),
h('textarea.twelve-word-phrase.letter-spacey', {
dataset: {
persistentFormId: 'wallet-seed',
},
- placeholder: 'Enter your secret twelve word phrase here to restore your vault.',
+ placeholder: t('secretPhrase'),
}),
// password
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box',
- placeholder: 'New Password (min 8 chars)',
+ placeholder: t('newPassword8Chars'),
dataset: {
persistentFormId: 'password',
},
@@ -66,7 +67,7 @@ RestoreVaultScreen.prototype.render = function () {
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box-confirm',
- placeholder: 'Confirm Password',
+ placeholder: t('confirmPassword'),
onKeyPress: this.createOnEnter.bind(this),
dataset: {
persistentFormId: 'password-confirmation',
@@ -93,16 +94,20 @@ RestoreVaultScreen.prototype.render = function () {
// cancel
h('button.primary', {
onClick: this.showInitializeMenu.bind(this),
- }, 'CANCEL'),
+ style: {
+ textTransform: 'uppercase',
+ },
+ }, t('cancel')),
// submit
h('button.primary', {
onClick: this.createNewVaultAndRestore.bind(this),
- }, 'OK'),
-
+ style: {
+ textTransform: 'uppercase',
+ },
+ }, t('ok')),
]),
])
-
)
}
@@ -131,21 +136,34 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
var passwordConfirmBox = document.getElementById('password-box-confirm')
var passwordConfirm = passwordConfirmBox.value
if (password.length < 8) {
- this.warning = 'Password not long enough'
+ this.warning = t('passwordNotLongEnough')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
if (password !== passwordConfirm) {
- this.warning = 'Passwords don\'t match'
+ this.warning = t('passwordsDontMatch')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
// check seed
var seedBox = document.querySelector('textarea.twelve-word-phrase')
var seed = seedBox.value.trim()
+
+ // true if the string has more than a space between words.
+ if (seed.split(' ').length > 1) {
+ this.warning = t('spaceBetween')
+ this.props.dispatch(actions.displayWarning(this.warning))
+ return
+ }
+ // true if seed contains a character that is not between a-z or a space
+ if (!seed.match(/^[a-z ]+$/)) {
+ this.warning = t('loweCaseWords')
+ this.props.dispatch(actions.displayWarning(this.warning))
+ return
+ }
if (seed.split(' ').length !== 12) {
- this.warning = 'seed phrases are 12 words long'
+ this.warning = t('seedPhraseReq')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
diff --git a/ui/app/main-container.js b/ui/app/main-container.js
index 292abcc3d..eed4bd164 100644
--- a/ui/app/main-container.js
+++ b/ui/app/main-container.js
@@ -32,19 +32,7 @@ MainContainer.prototype.render = function () {
return h(Settings, {key: 'config'})
default:
log.debug('rendering locked screen')
- contents = {
- component: UnlockScreen,
- style: {
- boxShadow: 'none',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- background: '#F7F7F7',
- // must force 100%, because lock screen is full-width
- width: '100%',
- },
- key: 'locked',
- }
+ return h('.unlock-screen-container', {}, h(UnlockScreen, { key: 'locked' }))
}
}
diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js
index 029d830ec..e6e02d057 100644
--- a/ui/app/reducers/metamask.js
+++ b/ui/app/reducers/metamask.js
@@ -38,12 +38,14 @@ function reduceMetamask (state, action) {
errors: {},
maxModeOn: false,
editingTransactionId: null,
+ forceGasMin: null,
},
coinOptions: {},
useBlockie: false,
featureFlags: {},
networkEndpointType: OLD_UI_NETWORK_TYPE,
isRevealingSeedWords: false,
+ welcomeScreenSeen: false,
}, state.metamask)
switch (action.type) {
@@ -296,6 +298,7 @@ function reduceMetamask (state, action) {
memo: '',
errors: {},
editingTransactionId: null,
+ forceGasMin: null,
},
})
@@ -347,6 +350,11 @@ function reduceMetamask (state, action) {
networkEndpointType: action.value,
})
+ case actions.CLOSE_WELCOME_SCREEN:
+ return extend(metamaskState, {
+ welcomeScreenSeen: true,
+ })
+
default:
return metamaskState
diff --git a/ui/app/selectors.js b/ui/app/selectors.js
index 5d2635775..d37c26f7e 100644
--- a/ui/app/selectors.js
+++ b/ui/app/selectors.js
@@ -18,6 +18,7 @@ const selectors = {
getCurrentAccountWithSendEtherInfo,
getGasPrice,
getGasLimit,
+ getForceGasMin,
getAddressBook,
getSendFrom,
getCurrentCurrency,
@@ -130,6 +131,10 @@ function getGasLimit (state) {
return state.metamask.send.gasLimit
}
+function getForceGasMin (state) {
+ return state.metamask.send.forceGasMin
+}
+
function getSendFrom (state) {
return state.metamask.send.from
}
diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js
index fc1df1f51..31118378d 100644
--- a/ui/app/send-v2.js
+++ b/ui/app/send-v2.js
@@ -1,6 +1,7 @@
const { inherits } = require('util')
const PersistentForm = require('../lib/persistent-form')
const h = require('react-hyperscript')
+const t = require('../i18n')
const ethAbi = require('ethereumjs-abi')
const ethUtil = require('ethereumjs-util')
@@ -42,6 +43,7 @@ function SendTransactionScreen () {
to: null,
amount: null,
},
+ gasLoadingError: false,
}
this.handleToChange = this.handleToChange.bind(this)
@@ -128,6 +130,10 @@ SendTransactionScreen.prototype.updateGas = function () {
.then(([gasPrice, gas]) => {
const newGasTotal = this.getGasTotal(gas, gasPrice)
updateGasTotal(newGasTotal)
+ this.setState({ gasLoadingError: false })
+ })
+ .catch(err => {
+ this.setState({ gasLoadingError: true })
})
} else {
const newGasTotal = this.getGasTotal(gasLimit, gasPrice)
@@ -180,13 +186,12 @@ SendTransactionScreen.prototype.componentDidUpdate = function (prevProps) {
SendTransactionScreen.prototype.renderHeader = function () {
const { selectedToken, clearSend, goHome } = this.props
- const tokenText = selectedToken ? 'tokens' : 'ETH'
return h('div.page-container__header', [
- h('div.page-container__title', selectedToken ? 'Send Tokens' : 'Send ETH'),
+ h('div.page-container__title', selectedToken ? t('sendTokens') : t('sendETH')),
- h('div.page-container__subtitle', `Only send ${tokenText} to an Ethereum address.`),
+ h('div.page-container__subtitle', t('onlySendToEtherAddress')),
h('div.page-container__header-close', {
onClick: () => {
@@ -257,11 +262,11 @@ SendTransactionScreen.prototype.handleToChange = function (to) {
let toError = null
if (!to) {
- toError = 'Required'
+ toError = t('required')
} else if (!isValidAddress(to)) {
- toError = 'Recipient address is invalid'
+ toError = t('invalidAddressRecipient')
} else if (to === from) {
- toError = 'From and To address cannot be the same'
+ toError = t('fromToSame')
}
updateSendTo(to)
@@ -277,9 +282,9 @@ SendTransactionScreen.prototype.renderToRow = function () {
h('div.send-v2__form-label', [
- 'To:',
+ t('to'),
- this.renderErrorMessage('to'),
+ this.renderErrorMessage(t('to')),
]),
@@ -377,11 +382,11 @@ SendTransactionScreen.prototype.validateAmount = function (value) {
)
if (conversionRate && !sufficientBalance) {
- amountError = 'Insufficient funds.'
+ amountError = t('insufficientFunds')
} else if (verifyTokenBalance && !sufficientTokens) {
- amountError = 'Insufficient tokens.'
+ amountError = t('insufficientTokens')
} else if (amountLessThanZero) {
- amountError = 'Can not send negative amounts of ETH.'
+ amountError = t('negativeETH')
}
updateSendErrors({ amount: amountError })
@@ -411,7 +416,7 @@ SendTransactionScreen.prototype.renderAmountRow = function () {
setMaxModeTo(true)
this.setAmountToMax()
},
- }, [ !maxModeOn ? 'Max' : '' ]),
+ }, [ !maxModeOn ? t('max') : '' ]),
]),
h('div.send-v2__form-field', [
@@ -436,10 +441,11 @@ SendTransactionScreen.prototype.renderGasRow = function () {
showCustomizeGasModal,
gasTotal,
} = this.props
+ const { gasLoadingError } = this.state
return h('div.send-v2__form-row', [
- h('div.send-v2__form-label', 'Gas fee:'),
+ h('div.send-v2__form-label', h('gasFee')),
h('div.send-v2__form-field', [
@@ -448,6 +454,7 @@ SendTransactionScreen.prototype.renderGasRow = function () {
conversionRate,
convertedCurrency,
onClick: showCustomizeGasModal,
+ gasLoadingError,
}),
]),
@@ -507,11 +514,11 @@ SendTransactionScreen.prototype.renderFooter = function () {
clearSend()
goHome()
},
- }, 'Cancel'),
+ }, t('cancel')),
h('button.btn-clear.page-container__footer-button', {
disabled: !noErrors || !gasTotal || missingTokenBalance,
onClick: event => this.onSubmit(event),
- }, 'Next'),
+ }, t('next')),
])
}
@@ -571,9 +578,11 @@ SendTransactionScreen.prototype.getEditedTx = function () {
data,
})
} else {
+ const data = unapprovedTxs[editingTransactionId].txParams.data
Object.assign(editingTx.txParams, {
value: ethUtil.addHexPrefix(amount),
to: ethUtil.addHexPrefix(to),
+ data,
})
}
diff --git a/ui/app/send.js b/ui/app/send.js
deleted file mode 100644
index 517b7690d..000000000
--- a/ui/app/send.js
+++ /dev/null
@@ -1,547 +0,0 @@
-// const { inherits } = require('util')
-// const PersistentForm = require('../lib/persistent-form')
-// const h = require('react-hyperscript')
-// const connect = require('react-redux').connect
-// const Identicon = require('./components/identicon')
-// const EnsInput = require('./components/ens-input')
-// const GasTooltip = require('./components/send/gas-tooltip')
-// const CurrencyToggle = require('./components/send/currency-toggle')
-// const GasFeeDisplay = require('./components/send/gas-fee-display')
-// const { getSelectedIdentity } = require('./selectors')
-
-// const {
-// showAccountsPage,
-// backToAccountDetail,
-// displayWarning,
-// hideWarning,
-// addToAddressBook,
-// signTx,
-// estimateGas,
-// getGasPrice,
-// } = require('./actions')
-// const { stripHexPrefix, addHexPrefix } = require('ethereumjs-util')
-// const { isHex, numericBalance, isValidAddress, allNull } = require('./util')
-// const { conversionUtil, conversionGreaterThan } = require('./conversion-util')
-
-// module.exports = connect(mapStateToProps)(SendTransactionScreen)
-
-// function mapStateToProps (state) {
-// const {
-// selectedAddress: address,
-// accounts,
-// identities,
-// network,
-// addressBook,
-// conversionRate,
-// currentBlockGasLimit: blockGasLimit,
-// } = state.metamask
-// const { warning } = state.appState
-// const selectedIdentity = getSelectedIdentity(state)
-// const account = accounts[address]
-
-// return {
-// address,
-// accounts,
-// identities,
-// network,
-// addressBook,
-// conversionRate,
-// blockGasLimit,
-// warning,
-// selectedIdentity,
-// error: warning && warning.split('.')[0],
-// account,
-// identity: identities[address],
-// balance: account ? account.balance : null,
-// }
-// }
-
-// inherits(SendTransactionScreen, PersistentForm)
-// function SendTransactionScreen () {
-// PersistentForm.call(this)
-
-// // [WIP] These are the bare minimum of tx props needed to sign a transaction
-// // We will need a few more for contract-related interactions
-// this.state = {
-// newTx: {
-// from: '',
-// to: '',
-// amountToSend: '0x0',
-// gasPrice: null,
-// gas: null,
-// amount: '0x0',
-// txData: null,
-// memo: '',
-// },
-// activeCurrency: 'USD',
-// tooltipIsOpen: false,
-// errors: {},
-// isValid: false,
-// }
-
-// this.back = this.back.bind(this)
-// this.closeTooltip = this.closeTooltip.bind(this)
-// this.onSubmit = this.onSubmit.bind(this)
-// this.setActiveCurrency = this.setActiveCurrency.bind(this)
-// this.toggleTooltip = this.toggleTooltip.bind(this)
-// this.validate = this.validate.bind(this)
-// this.getAmountToSend = this.getAmountToSend.bind(this)
-// this.setErrorsFor = this.setErrorsFor.bind(this)
-// this.clearErrorsFor = this.clearErrorsFor.bind(this)
-
-// this.renderFromInput = this.renderFromInput.bind(this)
-// this.renderToInput = this.renderToInput.bind(this)
-// this.renderAmountInput = this.renderAmountInput.bind(this)
-// this.renderGasInput = this.renderGasInput.bind(this)
-// this.renderMemoInput = this.renderMemoInput.bind(this)
-// this.renderErrorMessage = this.renderErrorMessage.bind(this)
-// }
-
-// SendTransactionScreen.prototype.componentWillMount = function () {
-// const { newTx } = this.state
-// const { address } = this.props
-
-// Promise.all([
-// this.props.dispatch(getGasPrice()),
-// this.props.dispatch(estimateGas({
-// from: address,
-// gas: '746a528800',
-// })),
-// ])
-// .then(([blockGasPrice, estimatedGas]) => {
-// console.log({ blockGasPrice, estimatedGas})
-// this.setState({
-// newTx: {
-// ...newTx,
-// gasPrice: blockGasPrice,
-// gas: estimatedGas,
-// },
-// })
-// })
-// }
-
-// SendTransactionScreen.prototype.renderErrorMessage = function(errorType, warning) {
-// const { errors } = this.state
-// const errorMessage = errors[errorType];
-
-// return errorMessage || warning
-// ? h('div.send-screen-input-wrapper__error-message', [ errorMessage || warning ])
-// : null
-// }
-
-// SendTransactionScreen.prototype.renderFromInput = function (from, identities) {
-// return h('div.send-screen-input-wrapper', [
-
-// h('div', 'From:'),
-
-// h('input.large-input.send-screen-input', {
-// list: 'accounts',
-// placeholder: 'Account',
-// value: from,
-// onChange: (event) => {
-// this.setState({
-// newTx: {
-// ...this.state.newTx,
-// from: event.target.value,
-// },
-// })
-// },
-// onBlur: () => this.setErrorsFor('from'),
-// onFocus: event => {
-// this.clearErrorsFor('from')
-// this.state.newTx.from && event.target.select()
-// },
-// }),
-
-// h('datalist#accounts', [
-// Object.entries(identities).map(([key, { address, name }]) => {
-// return h('option', {
-// value: address,
-// label: name,
-// key: address,
-// })
-// }),
-// ]),
-
-// this.renderErrorMessage('from'),
-
-// ])
-// }
-
-// SendTransactionScreen.prototype.renderToInput = function (to, identities, addressBook) {
-// return h('div.send-screen-input-wrapper', [
-
-// h('div', 'To:'),
-
-// h('input.large-input.send-screen-input', {
-// name: 'address',
-// list: 'addresses',
-// placeholder: 'Address',
-// value: to,
-// onChange: (event) => {
-// this.setState({
-// newTx: {
-// ...this.state.newTx,
-// to: event.target.value,
-// },
-// })
-// },
-// onBlur: () => {
-// this.setErrorsFor('to')
-// },
-// onFocus: event => {
-// this.clearErrorsFor('to')
-// this.state.newTx.to && event.target.select()
-// },
-// }),
-
-// h('datalist#addresses', [
-// // Corresponds to the addresses owned.
-// ...Object.entries(identities).map(([key, { address, name }]) => {
-// return h('option', {
-// value: address,
-// label: name,
-// key: address,
-// })
-// }),
-// // Corresponds to previously sent-to addresses.
-// ...addressBook.map(({ address, name }) => {
-// return h('option', {
-// value: address,
-// label: name,
-// key: address,
-// })
-// }),
-// ]),
-
-// this.renderErrorMessage('to'),
-
-// ])
-// }
-
-// SendTransactionScreen.prototype.renderAmountInput = function (activeCurrency) {
-// return h('div.send-screen-input-wrapper', [
-
-// h('div.send-screen-amount-labels', [
-// h('span', 'Amount'),
-// h(CurrencyToggle, {
-// activeCurrency,
-// onClick: (newCurrency) => this.setActiveCurrency(newCurrency),
-// }), // holding on icon from design
-// ]),
-
-// h('input.large-input.send-screen-input', {
-// placeholder: `0 ${activeCurrency}`,
-// type: 'number',
-// onChange: (event) => {
-// const amountToSend = event.target.value
-// ? this.getAmountToSend(event.target.value)
-// : '0x0'
-
-// this.setState({
-// newTx: Object.assign(
-// this.state.newTx,
-// {
-// amount: event.target.value,
-// amountToSend: amountToSend,
-// }
-// ),
-// })
-// },
-// onBlur: () => {
-// this.setErrorsFor('amount')
-// },
-// onFocus: () => this.clearErrorsFor('amount'),
-// }),
-
-// this.renderErrorMessage('amount'),
-
-// ])
-// }
-
-// SendTransactionScreen.prototype.renderGasInput = function (gasPrice, gas, activeCurrency, conversionRate, blockGasLimit) {
-// return h('div.send-screen-input-wrapper', [
-// this.state.tooltipIsOpen && h(GasTooltip, {
-// className: 'send-tooltip',
-// gasPrice,
-// gasLimit: gas,
-// onClose: this.closeTooltip,
-// onFeeChange: ({gasLimit, gasPrice}) => {
-// this.setState({
-// newTx: {
-// ...this.state.newTx,
-// gas: gasLimit,
-// gasPrice,
-// },
-// })
-// },
-// }),
-
-// h('div.send-screen-gas-labels', [
-// h('span', [
-// h('i.fa.fa-bolt'),
-// 'Gas fee:',
-// ]),
-// h('span', 'What\'s this?'),
-// ]),
-
-// // TODO: handle loading time when switching to USD
-// h('div.large-input.send-screen-gas-input', {}, [
-// h(GasFeeDisplay, {
-// activeCurrency,
-// conversionRate,
-// gas,
-// gasPrice,
-// blockGasLimit,
-// }),
-// h('div.send-screen-gas-input-customize', {
-// onClick: this.toggleTooltip,
-// }, [
-// 'Customize',
-// ]),
-// ]),
-
-// ])
-// }
-
-// SendTransactionScreen.prototype.renderMemoInput = function () {
-// return h('div.send-screen-input-wrapper', [
-// h('div', 'Transaction memo (optional)'),
-// h('input.large-input.send-screen-input', {
-// onChange: () => {
-// this.setState({
-// newTx: Object.assign(
-// this.state.newTx,
-// {
-// memo: event.target.value,
-// }
-// ),
-// })
-// },
-// }),
-// ])
-// }
-
-// SendTransactionScreen.prototype.render = function () {
-// this.persistentFormParentId = 'send-tx-form'
-
-// const props = this.props
-// const {
-// warning,
-// identities,
-// addressBook,
-// conversionRate,
-// } = props
-
-// const {
-// blockGasLimit,
-// newTx,
-// activeCurrency,
-// isValid,
-// } = this.state
-// const { gas, gasPrice } = newTx
-
-// return (
-
-// h('div.send-screen-wrapper', [
-// // Main Send token Card
-// h('div.send-screen-card', [
-
-// h('img.send-eth-icon', { src: '../images/eth_logo.svg' }),
-
-// h('div.send-screen__title', 'Send'),
-
-// h('div.send-screen__subtitle', 'Send Ethereum to anyone with an Ethereum account'),
-
-// this.renderFromInput(this.state.newTx.from, identities),
-
-// this.renderToInput(this.state.newTx.to, identities, addressBook),
-
-// this.renderAmountInput(activeCurrency),
-
-// this.renderGasInput(
-// gasPrice || '0x0',
-// gas || '0x0',
-// activeCurrency,
-// conversionRate,
-// blockGasLimit
-// ),
-
-// this.renderMemoInput(),
-
-// this.renderErrorMessage(null, warning),
-
-// ]),
-
-// // Buttons underneath card
-// h('section.flex-column.flex-center', [
-// h('button.btn-secondary.send-screen__send-button', {
-// className: !isValid && 'send-screen__send-button__disabled',
-// onClick: (event) => isValid && this.onSubmit(event),
-// }, 'Next'),
-// h('button.btn-tertiary.send-screen__cancel-button', {
-// onClick: this.back,
-// }, 'Cancel'),
-// ]),
-// ])
-
-// )
-// }
-
-// SendTransactionScreen.prototype.toggleTooltip = function () {
-// this.setState({ tooltipIsOpen: !this.state.tooltipIsOpen })
-// }
-
-// SendTransactionScreen.prototype.closeTooltip = function () {
-// this.setState({ tooltipIsOpen: false })
-// }
-
-// SendTransactionScreen.prototype.setActiveCurrency = function (newCurrency) {
-// this.setState({ activeCurrency: newCurrency })
-// }
-
-// SendTransactionScreen.prototype.back = function () {
-// var address = this.props.address
-// this.props.dispatch(backToAccountDetail(address))
-// }
-
-// SendTransactionScreen.prototype.validate = function (balance, amountToSend, { to, from }) {
-// const sufficientBalance = conversionGreaterThan(
-// {
-// value: balance,
-// fromNumericBase: 'hex',
-// },
-// {
-// value: amountToSend,
-// fromNumericBase: 'hex',
-// },
-// )
-
-// const amountLessThanZero = conversionGreaterThan(
-// {
-// value: 0,
-// fromNumericBase: 'dec',
-// },
-// {
-// value: amountToSend,
-// fromNumericBase: 'hex',
-// },
-// )
-
-// const errors = {}
-
-// if (!sufficientBalance) {
-// errors.amount = 'Insufficient funds.'
-// }
-
-// if (amountLessThanZero) {
-// errors.amount = 'Can not send negative amounts of ETH.'
-// }
-
-// if (!from) {
-// errors.from = 'Required'
-// }
-
-// if (from && !isValidAddress(from)) {
-// errors.from = 'Sender address is invalid.'
-// }
-
-// if (!to) {
-// errors.to = 'Required'
-// }
-
-// if (to && !isValidAddress(to)) {
-// errors.to = 'Recipient address is invalid.'
-// }
-
-// // if (txData && !isHex(stripHexPrefix(txData))) {
-// // message = 'Transaction data must be hex string.'
-// // return this.props.dispatch(displayWarning(message))
-// // }
-
-// return {
-// isValid: allNull(errors),
-// errors,
-// }
-// }
-
-// SendTransactionScreen.prototype.getAmountToSend = function (amount) {
-// const { activeCurrency } = this.state
-// const { conversionRate } = this.props
-
-// return conversionUtil(amount, {
-// fromNumericBase: 'dec',
-// toNumericBase: 'hex',
-// fromCurrency: activeCurrency,
-// toCurrency: 'ETH',
-// toDenomination: 'WEI',
-// conversionRate,
-// invertConversionRate: activeCurrency !== 'ETH',
-// })
-// }
-
-// SendTransactionScreen.prototype.setErrorsFor = function (field) {
-// const { balance } = this.props
-// const { newTx, errors: previousErrors } = this.state
-// const { amountToSend } = newTx
-
-// const {
-// isValid,
-// errors: newErrors
-// } = this.validate(balance, amountToSend, newTx)
-
-// const nextErrors = Object.assign({}, previousErrors, {
-// [field]: newErrors[field] || null
-// })
-
-// if (!isValid) {
-// this.setState({
-// errors: nextErrors,
-// isValid,
-// })
-// }
-// }
-
-// SendTransactionScreen.prototype.clearErrorsFor = function (field) {
-// const { errors: previousErrors } = this.state
-// const nextErrors = Object.assign({}, previousErrors, {
-// [field]: null
-// })
-
-// this.setState({
-// errors: nextErrors,
-// isValid: allNull(nextErrors),
-// })
-// }
-
-// SendTransactionScreen.prototype.onSubmit = function (event) {
-// event.preventDefault()
-// const { warning, balance } = this.props
-// const state = this.state || {}
-
-// const recipient = state.newTx.to
-// const sender = state.newTx.from
-// const nickname = state.nickname || ' '
-
-// // TODO: convert this to hex when created and include it in send
-// const txData = state.newTx.memo
-
-// this.props.dispatch(hideWarning())
-
-// this.props.dispatch(addToAddressBook(recipient, nickname))
-
-// var txParams = {
-// from: this.state.newTx.from,
-// to: this.state.newTx.to,
-
-// value: this.state.newTx.amountToSend,
-
-// gas: this.state.newTx.gas,
-// gasPrice: this.state.newTx.gasPrice,
-// }
-
-// if (recipient) txParams.to = addHexPrefix(recipient)
-// if (txData) txParams.data = txData
-
-// this.props.dispatch(signTx(txParams))
-// }
diff --git a/ui/app/settings.js b/ui/app/settings.js
index 466f739d5..105cbb40b 100644
--- a/ui/app/settings.js
+++ b/ui/app/settings.js
@@ -10,6 +10,7 @@ const TabBar = require('./components/tab-bar')
const SimpleDropdown = require('./components/dropdowns/simple-dropdown')
const ToggleButton = require('react-toggle-button')
const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
+const t = require('../i18n')
const getInfuraCurrencyOptions = () => {
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
@@ -44,8 +45,8 @@ class Settings extends Component {
return h('div.settings__tabs', [
h(TabBar, {
tabs: [
- { content: 'Settings', key: 'settings' },
- { content: 'Info', key: 'info' },
+ { content: t('settings'), key: 'settings' },
+ { content: t('info'), key: 'info' },
],
defaultTab: activeTab,
tabSelected: key => this.setState({ activeTab: key }),
@@ -58,7 +59,7 @@ class Settings extends Component {
return h('div.settings__content-row', [
h('div.settings__content-item', [
- h('span', 'Use Blockies Identicon'),
+ h('span', t('blockiesIdenticon')),
]),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
@@ -78,13 +79,13 @@ class Settings extends Component {
return h('div.settings__content-row', [
h('div.settings__content-item', [
- h('span', 'Current Conversion'),
+ h('span', t('currentConversion')),
h('span.settings__content-description', `Updated ${Date(conversionDate)}`),
]),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
h(SimpleDropdown, {
- placeholder: 'Select Currency',
+ placeholder: t('selectCurrency'),
options: getInfuraCurrencyOptions(),
selectedOption: currentCurrency,
onSelect: newCurrency => setCurrentCurrency(newCurrency),
@@ -101,31 +102,31 @@ class Settings extends Component {
switch (provider.type) {
case 'mainnet':
- title = 'Current Network'
- value = 'Main Ethereum Network'
+ title = t('currentNetwork')
+ value = t('mainnet')
color = '#038789'
break
case 'ropsten':
- title = 'Current Network'
- value = 'Ropsten Test Network'
+ title = t('currentNetwork')
+ value = t('ropsten')
color = '#e91550'
break
case 'kovan':
- title = 'Current Network'
- value = 'Kovan Test Network'
+ title = t('currentNetwork')
+ value = t('kovan')
color = '#690496'
break
case 'rinkeby':
- title = 'Current Network'
- value = 'Rinkeby Test Network'
+ title = t('currentNetwork')
+ value = t('rinkeby')
color = '#ebb33f'
break
default:
- title = 'Current RPC'
+ title = t('currentRpc')
value = provider.rpcTarget
}
@@ -146,12 +147,12 @@ class Settings extends Component {
return (
h('div.settings__content-row', [
h('div.settings__content-item', [
- h('span', 'New RPC URL'),
+ h('span', t('newRPC')),
]),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
h('input.settings__input', {
- placeholder: 'New RPC URL',
+ placeholder: t('newRPC'),
onChange: event => this.setState({ newRpc: event.target.value }),
onKeyPress: event => {
if (event.key === 'Enter') {
@@ -164,7 +165,7 @@ class Settings extends Component {
event.preventDefault()
this.validateRpc(this.state.newRpc)
},
- }, 'Save'),
+ }, t('save')),
]),
]),
])
@@ -180,9 +181,9 @@ class Settings extends Component {
const appendedRpc = `http://${newRpc}`
if (validUrl.isWebUri(appendedRpc)) {
- displayWarning('URIs require the appropriate HTTP/HTTPS prefix.')
+ displayWarning(t('uriErrorMsg'))
} else {
- displayWarning('Invalid RPC URI')
+ displayWarning(t('invalidRPC'))
}
}
}
@@ -191,10 +192,10 @@ class Settings extends Component {
return (
h('div.settings__content-row', [
h('div.settings__content-item', [
- h('div', 'State Logs'),
+ h('div', t('stateLogs')),
h(
'div.settings__content-description',
- 'State logs contain your public account addresses and sent transactions.'
+ t('stateLogsDescription')
),
]),
h('div.settings__content-item', [
@@ -203,13 +204,13 @@ class Settings extends Component {
onClick (event) {
window.logStateString((err, result) => {
if (err) {
- this.state.dispatch(actions.displayWarning('Error in retrieving state logs.'))
+ this.state.dispatch(actions.displayWarning(t('stateLogError')))
} else {
exportAsFile('MetaMask State Logs.json', result)
}
})
},
- }, 'Download State Logs'),
+ }, t('downloadStateLogs')),
]),
]),
])
@@ -221,7 +222,7 @@ class Settings extends Component {
return (
h('div.settings__content-row', [
- h('div.settings__content-item', 'Reveal Seed Words'),
+ h('div.settings__content-item', t('revealSeedWords')),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
h('button.settings__clear-button.settings__clear-button--red', {
@@ -229,7 +230,7 @@ class Settings extends Component {
event.preventDefault()
revealSeedConfirmation()
},
- }, 'Reveal Seed Words'),
+ }, t('revealSeedWords')),
]),
]),
])
@@ -241,7 +242,7 @@ class Settings extends Component {
return (
h('div.settings__content-row', [
- h('div.settings__content-item', 'Use old UI'),
+ h('div.settings__content-item', t('useOldUI')),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
h('button.settings__clear-button.settings__clear-button--orange', {
@@ -249,7 +250,7 @@ class Settings extends Component {
event.preventDefault()
setFeatureFlagToBeta()
},
- }, 'Use old UI'),
+ }, t('useOldUI')),
]),
]),
])
@@ -260,7 +261,7 @@ class Settings extends Component {
const { showResetAccountConfirmationModal } = this.props
return h('div.settings__content-row', [
- h('div.settings__content-item', 'Reset Account'),
+ h('div.settings__content-item', t('resetAccount')),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
h('button.settings__clear-button.settings__clear-button--orange', {
@@ -268,7 +269,7 @@ class Settings extends Component {
event.preventDefault()
showResetAccountConfirmationModal()
},
- }, 'Reset Account'),
+ }, t('resetAccount')),
]),
]),
])
@@ -303,13 +304,13 @@ class Settings extends Component {
renderInfoLinks () {
return (
h('div.settings__content-item.settings__content-item--without-height', [
- h('div.settings__info-link-header', 'Links'),
+ h('div.settings__info-link-header', t('links')),
h('div.settings__info-link-item', [
h('a', {
href: 'https://metamask.io/privacy.html',
target: '_blank',
}, [
- h('span.settings__info-link', 'Privacy Policy'),
+ h('span.settings__info-link', t('privacyMsg')),
]),
]),
h('div.settings__info-link-item', [
@@ -317,7 +318,7 @@ class Settings extends Component {
href: 'https://metamask.io/terms.html',
target: '_blank',
}, [
- h('span.settings__info-link', 'Terms of Use'),
+ h('span.settings__info-link', t('terms')),
]),
]),
h('div.settings__info-link-item', [
@@ -325,7 +326,7 @@ class Settings extends Component {
href: 'https://metamask.io/attributions.html',
target: '_blank',
}, [
- h('span.settings__info-link', 'Attributions'),
+ h('span.settings__info-link', t('attributions')),
]),
]),
h('hr.settings__info-separator'),
@@ -334,7 +335,7 @@ class Settings extends Component {
href: 'https://support.metamask.io',
target: '_blank',
}, [
- h('span.settings__info-link', 'Visit our Support Center'),
+ h('span.settings__info-link', t('supportCenter')),
]),
]),
h('div.settings__info-link-item', [
@@ -342,7 +343,7 @@ class Settings extends Component {
href: 'https://metamask.io/',
target: '_blank',
}, [
- h('span.settings__info-link', 'Visit our web site'),
+ h('span.settings__info-link', t('visitWebSite')),
]),
]),
h('div.settings__info-link-item', [
@@ -350,7 +351,7 @@ class Settings extends Component {
target: '_blank',
href: 'mailto:help@metamask.io?subject=Feedback',
}, [
- h('span.settings__info-link', 'Email us!'),
+ h('span.settings__info-link', t('emailUs')),
]),
]),
])
@@ -372,7 +373,7 @@ class Settings extends Component {
h('div.settings__info-item', [
h(
'div.settings__info-about',
- 'MetaMask is designed and built in California.'
+ t('builtInCalifornia')
),
]),
]),
@@ -445,3 +446,4 @@ const mapDispatchToProps = dispatch => {
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(Settings)
+
diff --git a/ui/app/template.js b/ui/app/template.js
deleted file mode 100644
index d15b30fd2..000000000
--- a/ui/app/template.js
+++ /dev/null
@@ -1,30 +0,0 @@
-const inherits = require('util').inherits
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-
-module.exports = connect(mapStateToProps)(COMPONENTNAME)
-
-function mapStateToProps (state) {
- return {}
-}
-
-inherits(COMPONENTNAME, Component)
-function COMPONENTNAME () {
- Component.call(this)
-}
-
-COMPONENTNAME.prototype.render = function () {
- const props = this.props
-
- return (
- h('div', {
- style: {
- background: 'blue',
- },
- }, [
- `Hello, ${props.sender}`,
- ])
- )
-}
-
diff --git a/ui/app/token-tracker.js b/ui/app/token-tracker.js
deleted file mode 100644
index e69de29bb..000000000
--- a/ui/app/token-tracker.js
+++ /dev/null
diff --git a/ui/app/unlock.js b/ui/app/unlock.js
index cafe3a859..322808619 100644
--- a/ui/app/unlock.js
+++ b/ui/app/unlock.js
@@ -5,6 +5,7 @@ const connect = require('react-redux').connect
const actions = require('./actions')
const getCaretCoordinates = require('textarea-caret')
const EventEmitter = require('events').EventEmitter
+const t = require('../i18n')
const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
const environmentType = require('../../app/scripts/lib/environment-type')
@@ -28,83 +29,72 @@ UnlockScreen.prototype.render = function () {
const state = this.props
const warning = state.warning
return (
- h('.flex-column', {
- style: {
- width: 'inherit',
- },
- }, [
- h('.unlock-screen.flex-column.flex-center.flex-grow', [
-
- h(Mascot, {
- animationEventEmitter: this.animationEventEmitter,
- }),
-
- h('h1', {
- style: {
- fontSize: '1.4em',
- textTransform: 'uppercase',
- color: '#7F8082',
- },
- }, 'MetaMask'),
-
- h('input.large-input', {
- type: 'password',
- id: 'password-box',
- placeholder: 'enter password',
- style: {
- background: 'white',
- },
- onKeyPress: this.onKeyPress.bind(this),
- onInput: this.inputChanged.bind(this),
- }),
-
- h('.error', {
- style: {
- display: warning ? 'block' : 'none',
- padding: '0 20px',
- textAlign: 'center',
- },
- }, warning),
-
- h('button.primary.cursor-pointer', {
- onClick: this.onSubmit.bind(this),
- style: {
- margin: 10,
- },
- }, 'Log In'),
- ]),
-
- h('.flex-row.flex-center.flex-grow', [
- h('p.pointer', {
- onClick: () => {
- this.props.dispatch(actions.markPasswordForgotten())
- if (environmentType() === 'popup') {
- global.platform.openExtensionInBrowser()
- }
- },
- style: {
- fontSize: '0.8em',
- color: 'rgb(247, 134, 28)',
- textDecoration: 'underline',
- },
- }, 'Restore from seed phrase'),
- ]),
-
- h('.flex-row.flex-center.flex-grow', [
- h('p.pointer', {
- onClick: () => {
- this.props.dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
- .then(() => this.props.dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE)))
- },
- style: {
- fontSize: '0.8em',
- color: '#aeaeae',
- textDecoration: 'underline',
- marginTop: '32px',
- },
- }, 'Use classic interface'),
- ]),
-
+ h('.unlock-screen', [
+
+ h(Mascot, {
+ animationEventEmitter: this.animationEventEmitter,
+ }),
+
+ h('h1', {
+ style: {
+ fontSize: '1.4em',
+ textTransform: 'uppercase',
+ color: '#7F8082',
+ },
+ }, t('appName')),
+
+ h('input.large-input', {
+ type: 'password',
+ id: 'password-box',
+ placeholder: 'enter password',
+ style: {
+ background: 'white',
+ },
+ onKeyPress: this.onKeyPress.bind(this),
+ onInput: this.inputChanged.bind(this),
+ }),
+
+ h('.error', {
+ style: {
+ display: warning ? 'block' : 'none',
+ padding: '0 20px',
+ textAlign: 'center',
+ },
+ }, warning),
+
+ h('button.primary.cursor-pointer', {
+ onClick: this.onSubmit.bind(this),
+ style: {
+ margin: 10,
+ },
+ }, t('login')),
+
+ h('p.pointer', {
+ onClick: () => {
+ this.props.dispatch(actions.markPasswordForgotten())
+ if (environmentType() === 'popup') {
+ global.platform.openExtensionInBrowser()
+ }
+ },
+ style: {
+ fontSize: '0.8em',
+ color: 'rgb(247, 134, 28)',
+ textDecoration: 'underline',
+ },
+ }, t('restoreFromSeed')),
+
+ h('p.pointer', {
+ onClick: () => {
+ this.props.dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
+ .then(() => this.props.dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE)))
+ },
+ style: {
+ fontSize: '0.8em',
+ color: '#aeaeae',
+ textDecoration: 'underline',
+ marginTop: '32px',
+ },
+ }, t('classicInterface')),
])
)
}
diff --git a/ui/app/welcome-screen.js b/ui/app/welcome-screen.js
new file mode 100644
index 000000000..cdbb6dba8
--- /dev/null
+++ b/ui/app/welcome-screen.js
@@ -0,0 +1,56 @@
+import EventEmitter from 'events'
+import h from 'react-hyperscript'
+import { Component } from 'react'
+import PropTypes from 'prop-types'
+import {connect} from 'react-redux'
+import {closeWelcomeScreen} from './actions'
+import Mascot from './components/mascot'
+
+class WelcomeScreen extends Component {
+ static propTypes = {
+ closeWelcomeScreen: PropTypes.func.isRequired,
+ }
+
+ constructor(props) {
+ super(props)
+ this.animationEventEmitter = new EventEmitter()
+ }
+
+ initiateAccountCreation = () => {
+ this.props.closeWelcomeScreen()
+ }
+
+ render () {
+ return h('div.welcome-screen', [
+
+ h('div.welcome-screen__info', [
+
+ h(Mascot, {
+ animationEventEmitter: this.animationEventEmitter,
+ width: '225',
+ height: '225',
+ }),
+
+ h('div.welcome-screen__info__header', 'Welcome to MetaMask Beta'),
+
+ h('div.welcome-screen__info__copy', 'MetaMask is a secure identity vault for Ethereum.'),
+
+ h('div.welcome-screen__info__copy', `It allows you to hold ether & tokens,
+ and serves as your bridge to decentralized applications.`),
+
+ h('button.welcome-screen__button', {
+ onClick: this.initiateAccountCreation,
+ }, 'Continue'),
+
+ ]),
+
+ ])
+ }
+}
+
+export default connect(
+ null,
+ dispatch => ({
+ closeWelcomeScreen: () => dispatch(closeWelcomeScreen()),
+ })
+)(WelcomeScreen)
diff --git a/ui/i18n.js b/ui/i18n.js
new file mode 100644
index 000000000..abfece426
--- /dev/null
+++ b/ui/i18n.js
@@ -0,0 +1,33 @@
+
+// cross-browser connection to extension i18n API
+
+const chrome = chrome || null
+const browser = browser || null
+const extension = require('extensionizer')
+var log = require('loglevel')
+window.log = log
+let getMessage
+
+if (extension.i18n && extension.i18n.getMessage) {
+ getMessage = extension.i18n.getMessage
+} else {
+ // fallback function
+ log.warn('browser.i18n API not available, calling back to english.')
+ const msg = require('../app/_locales/en/messages.json')
+ getMessage = function (key, substitutions) {
+ if (!msg[key]) {
+ log.error(key)
+ throw new Error(key)
+ }
+ let phrase = msg[key].message
+ if (substitutions && substitutions.length) {
+ phrase = phrase.replace(/\$1/g, substitutions[0])
+ if (substitutions.length > 1) {
+ phrase = phrase.replace(/\$2/g, substitutions[1])
+ }
+ }
+ return phrase
+ }
+}
+
+module.exports = getMessage
diff --git a/ui/lib/contract-namer.js b/ui/lib/contract-namer.js
deleted file mode 100644
index f05e770cc..000000000
--- a/ui/lib/contract-namer.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/* CONTRACT NAMER
- *
- * Takes an address,
- * Returns a nicname if we have one stored,
- * otherwise returns null.
- */
-
-const contractMap = require('eth-contract-metadata')
-const ethUtil = require('ethereumjs-util')
-
-module.exports = function (addr, identities = {}) {
- const checksummed = ethUtil.toChecksumAddress(addr)
- if (contractMap[checksummed] && contractMap[checksummed].name) {
- return contractMap[checksummed].name
- }
-
- const address = addr.toLowerCase()
- const ids = hashFromIdentities(identities)
- return addrFromHash(address, ids)
-}
-
-function hashFromIdentities (identities) {
- const result = {}
- for (const key in identities) {
- result[key] = identities[key].name
- }
- return result
-}
-
-function addrFromHash (addr, hash) {
- const address = addr.toLowerCase()
- return hash[address] || null
-}
diff --git a/yarn.lock b/yarn.lock
index 888175c92..7512e10db 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -155,6 +155,13 @@ acorn-jsx@^3.0.0:
dependencies:
acorn "^3.0.4"
+acorn-node@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.3.0.tgz#5f86d73346743810ef1269b901dbcbded020861b"
+ dependencies:
+ acorn "^5.4.1"
+ xtend "^4.0.1"
+
acorn@5.X, acorn@^5.0.0, acorn@^5.0.3, acorn@^5.1.2, acorn@^5.2.1:
version "5.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.3.0.tgz#7446d39459c54fb49a80e6ee6478149b940ec822"
@@ -167,6 +174,10 @@ acorn@^4.0.3:
version "4.0.13"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
+acorn@^5.4.1:
+ version "5.5.3"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"
+
addressparser@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746"
@@ -278,6 +289,12 @@ ansi-styles@^3.1.0:
dependencies:
color-convert "^1.9.0"
+ansi-styles@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+ dependencies:
+ color-convert "^1.9.0"
+
ansi-wrap@0.1.0, ansi-wrap@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf"
@@ -409,10 +426,6 @@ array-initial@^1.0.0:
array-slice "^1.0.0"
is-number "^4.0.0"
-array-iterate@^1.0.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/array-iterate/-/array-iterate-1.1.1.tgz#865bf7f8af39d6b0982c60902914ac76bc0108f6"
-
array-last@^1.1.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/array-last/-/array-last-1.3.0.tgz#7aa77073fec565ddab2493f5f88185f404a9d336"
@@ -623,15 +636,15 @@ autoprefixer@^6.0.0:
postcss "^5.2.16"
postcss-value-parser "^3.2.3"
-autoprefixer@^7.0.0, autoprefixer@^7.1.2:
- version "7.2.3"
- resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-7.2.3.tgz#c2841e38b7940c2d0a9bbffd72c75f33637854f8"
+autoprefixer@^8.0.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-8.1.0.tgz#374cf35be1c0e8fce97408d876f95f66f5cb4641"
dependencies:
- browserslist "^2.10.0"
- caniuse-lite "^1.0.30000783"
+ browserslist "^3.1.1"
+ caniuse-lite "^1.0.30000810"
normalize-range "^0.1.2"
num2fraction "^1.2.2"
- postcss "^6.0.14"
+ postcss "^6.0.19"
postcss-value-parser "^3.2.3"
await-semaphore@^0.1.1:
@@ -1853,7 +1866,7 @@ browserify-zlib@^0.2.0, browserify-zlib@~0.2.0:
dependencies:
pako "~1.0.5"
-browserify@^14.0.0, browserify@^14.4.0, browserify@^14.5.0:
+browserify@^14.0.0, browserify@^14.5.0:
version "14.5.0"
resolved "https://registry.yarnpkg.com/browserify/-/browserify-14.5.0.tgz#0bbbce521acd6e4d1d54d8e9365008efb85a9cc5"
dependencies:
@@ -1905,6 +1918,59 @@ browserify@^14.0.0, browserify@^14.4.0, browserify@^14.5.0:
vm-browserify "~0.0.1"
xtend "^4.0.0"
+browserify@^16.1.1:
+ version "16.1.1"
+ resolved "https://registry.yarnpkg.com/browserify/-/browserify-16.1.1.tgz#7905ec07e0147c4d90f92001944050a6e1c2844e"
+ dependencies:
+ JSONStream "^1.0.3"
+ assert "^1.4.0"
+ browser-pack "^6.0.1"
+ browser-resolve "^1.11.0"
+ browserify-zlib "~0.2.0"
+ buffer "^5.0.2"
+ cached-path-relative "^1.0.0"
+ concat-stream "^1.6.0"
+ console-browserify "^1.1.0"
+ constants-browserify "~1.0.0"
+ crypto-browserify "^3.0.0"
+ defined "^1.0.0"
+ deps-sort "^2.0.0"
+ domain-browser "^1.2.0"
+ duplexer2 "~0.1.2"
+ events "^2.0.0"
+ glob "^7.1.0"
+ has "^1.0.0"
+ htmlescape "^1.1.0"
+ https-browserify "^1.0.0"
+ inherits "~2.0.1"
+ insert-module-globals "^7.0.0"
+ labeled-stream-splicer "^2.0.0"
+ mkdirp "^0.5.0"
+ module-deps "^6.0.0"
+ os-browserify "~0.3.0"
+ parents "^1.0.1"
+ path-browserify "~0.0.0"
+ process "~0.11.0"
+ punycode "^1.3.2"
+ querystring-es3 "~0.2.0"
+ read-only-stream "^2.0.0"
+ readable-stream "^2.0.2"
+ resolve "^1.1.4"
+ shasum "^1.0.0"
+ shell-quote "^1.6.1"
+ stream-browserify "^2.0.0"
+ stream-http "^2.0.0"
+ string_decoder "~1.0.0"
+ subarg "^1.0.0"
+ syntax-error "^1.1.1"
+ through2 "^2.0.0"
+ timers-browserify "^1.0.1"
+ tty-browserify "0.0.1"
+ url "~0.11.0"
+ util "~0.10.1"
+ vm-browserify "~0.0.1"
+ xtend "^4.0.0"
+
browserslist@^1.1.1, browserslist@^1.1.3, browserslist@^1.7.6:
version "1.7.7"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
@@ -1912,13 +1978,20 @@ browserslist@^1.1.1, browserslist@^1.1.3, browserslist@^1.7.6:
caniuse-db "^1.0.30000639"
electron-to-chromium "^1.2.7"
-browserslist@^2.1.2, browserslist@^2.10.0:
+browserslist@^2.1.2:
version "2.10.0"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.10.0.tgz#bac5ee1cc69ca9d96403ffb8a3abdc5b6aed6346"
dependencies:
caniuse-lite "^1.0.30000780"
electron-to-chromium "^1.3.28"
+browserslist@^3.1.1:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.1.2.tgz#893f29399d640ed35fe06bacd7eb1d78609a47e5"
+ dependencies:
+ caniuse-lite "^1.0.30000813"
+ electron-to-chromium "^1.3.36"
+
bs58@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-2.0.1.tgz#55908d58f1982aba2008fa1bed8f91998a29bf8d"
@@ -2054,14 +2127,6 @@ camelcase-keys@^2.0.0:
camelcase "^2.0.0"
map-obj "^1.0.0"
-camelcase-keys@^4.0.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77"
- dependencies:
- camelcase "^4.1.0"
- map-obj "^2.0.0"
- quick-lru "^1.0.0"
-
camelcase@^1.0.2:
version "1.2.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
@@ -2082,10 +2147,14 @@ caniuse-db@^1.0.30000187, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
version "1.0.30000784"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000784.tgz#1be95012d9489c7719074f81aee57dbdffe6361b"
-caniuse-lite@^1.0.30000780, caniuse-lite@^1.0.30000783:
+caniuse-lite@^1.0.30000780:
version "1.0.30000784"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000784.tgz#129ced74e9a1280a441880b6cd2bce30ef59e6c0"
+caniuse-lite@^1.0.30000810, caniuse-lite@^1.0.30000813:
+ version "1.0.30000814"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000814.tgz#73eb6925ac2e54d495218f1ea0007da3940e488b"
+
caseless@~0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7"
@@ -2094,10 +2163,6 @@ caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
-ccount@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.2.tgz#53b6a2f815bb77b9c2871f7b9a72c3a25f1d8e89"
-
center-align@^0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad"
@@ -2156,14 +2221,18 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0:
escape-string-regexp "^1.0.5"
supports-color "^4.0.0"
+chalk@^2.3.1:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65"
+ dependencies:
+ ansi-styles "^3.2.1"
+ escape-string-regexp "^1.0.5"
+ supports-color "^5.3.0"
+
change-emitter@^0.1.2:
version "0.1.6"
resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515"
-character-entities-html4@^1.0.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.1.tgz#359a2a4a0f7e29d3dc2ac99bdbe21ee39438ea50"
-
character-entities-legacy@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.1.tgz#f40779df1a101872bb510a3d295e1fccf147202f"
@@ -2679,15 +2748,6 @@ cosmiconfig@^2.1.1:
parse-json "^2.2.0"
require-from-string "^1.1.0"
-cosmiconfig@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-3.1.0.tgz#640a94bf9847f321800403cd273af60665c73397"
- dependencies:
- is-directory "^0.3.1"
- js-yaml "^3.9.0"
- parse-json "^3.0.0"
- require-from-string "^2.0.1"
-
coveralls@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.0.0.tgz#22ef730330538080d29b8c151dc9146afde88a99"
@@ -2733,6 +2793,13 @@ create-react-class@^15.6.0:
loose-envify "^1.3.1"
object-assign "^4.1.1"
+cross-env@^5.1.4:
+ version "5.1.4"
+ resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.1.4.tgz#f61c14291f7cc653bb86457002ea80a04699d022"
+ dependencies:
+ cross-spawn "^5.1.0"
+ is-windows "^1.0.0"
+
cross-spawn@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
@@ -2869,10 +2936,6 @@ cycle@1.0.x:
version "1.0.3"
resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2"
-cyclist@~0.2.2:
- version "0.2.2"
- resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
-
d3@^3.4.3:
version "3.5.17"
resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
@@ -2905,6 +2968,14 @@ dateformat@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062"
+debounce-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/debounce-stream/-/debounce-stream-2.0.0.tgz#1e33400ccff015abd8ec330661a562b68410b08f"
+ dependencies:
+ debounce "^1.0.0"
+ duplexer "^0.1.1"
+ through "^2.3.6"
+
debounce@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.1.0.tgz#6a1a4ee2a9dc4b7c24bb012558dbcdb05b37f408"
@@ -2939,20 +3010,13 @@ debug@2.3.3:
dependencies:
ms "0.7.2"
-debug@3.1.0, debug@3.X, debug@^3.0.0, debug@^3.0.1, debug@^3.1.0, debug@~3.1.0:
+debug@3.1.0, debug@3.X, debug@^3.0.1, debug@^3.1.0, debug@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
-decamelize-keys@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9"
- dependencies:
- decamelize "^1.1.0"
- map-obj "^1.0.0"
-
-decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2:
+decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -3172,6 +3236,14 @@ detective@^4.0.0, detective@^4.3.1:
acorn "^5.2.1"
defined "^1.0.0"
+detective@^5.0.2:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/detective/-/detective-5.1.0.tgz#7a20d89236d7b331ccea65832e7123b5551bb7cb"
+ dependencies:
+ acorn-node "^1.3.0"
+ defined "^1.0.0"
+ minimist "^1.1.1"
+
di@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
@@ -3192,13 +3264,6 @@ diffie-hellman@^5.0.0:
miller-rabin "^4.0.0"
randombytes "^2.0.0"
-dir-glob@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034"
- dependencies:
- arrify "^1.0.1"
- path-type "^3.0.0"
-
disc@^1.3.2:
version "1.3.3"
resolved "https://registry.yarnpkg.com/disc/-/disc-1.3.3.tgz#61d455180c2a115468bb85015a33e71a82fc02c2"
@@ -3289,6 +3354,10 @@ domain-browser@^1.1.1, domain-browser@~1.1.0:
version "1.1.7"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
+domain-browser@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
+
domelementtype@1, domelementtype@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
@@ -3325,12 +3394,6 @@ domutils@^1.5.1:
dom-serializer "0"
domelementtype "1"
-dot-prop@^4.1.1:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
- dependencies:
- is-obj "^1.0.0"
-
double-ended-queue@^2.1.0-0:
version "2.1.0-0"
resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c"
@@ -3405,6 +3468,10 @@ electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.28:
dependencies:
electron-releases "^2.1.0"
+electron-to-chromium@^1.3.36:
+ version "1.3.37"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.37.tgz#4a92734e0044c8cf0b1553be57eae21a4c6e5fab"
+
elliptic@^6.0.0, elliptic@^6.2.3:
version "6.4.0"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
@@ -3594,7 +3661,7 @@ errno@^0.1.3, errno@~0.1.1:
dependencies:
prr "~1.0.1"
-error-ex@^1.2.0, error-ex@^1.3.1:
+error-ex@^1.2.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc"
dependencies:
@@ -4350,6 +4417,10 @@ events@^1.0.0, events@^1.1.1, events@~1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
+events@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/events/-/events-2.0.0.tgz#cbbb56bf3ab1ac18d71c43bb32c86255062769f2"
+
evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
@@ -4575,7 +4646,7 @@ falafel@^2.1.0:
isarray "0.0.1"
object-keys "^1.0.6"
-fancy-log@^1.1.0:
+fancy-log@^1.1.0, fancy-log@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.2.tgz#f41125e3d84f2e7d89a43d06d958c8f78be16be1"
dependencies:
@@ -4718,7 +4789,7 @@ find-up@^1.0.0:
path-exists "^2.0.0"
pinkie-promise "^2.0.0"
-find-up@^2.0.0, find-up@^2.1.0:
+find-up@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
dependencies:
@@ -4891,13 +4962,6 @@ fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
-from2@^2.1.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
- dependencies:
- inherits "^2.0.1"
- readable-stream "^2.0.0"
-
from@~0:
version "0.1.7"
resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
@@ -5049,7 +5113,7 @@ get-stdin@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
-get-stdin@^5.0.0, get-stdin@^5.0.1:
+get-stdin@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398"
@@ -5242,17 +5306,6 @@ globby@^6.0.0, globby@^6.1.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
-globby@^7.0.0:
- version "7.1.1"
- resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680"
- dependencies:
- array-union "^1.0.1"
- dir-glob "^2.0.0"
- glob "^7.1.2"
- ignore "^3.3.5"
- pify "^3.0.0"
- slash "^1.0.0"
-
globjoin@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43"
@@ -5271,12 +5324,6 @@ glogg@^1.0.0:
dependencies:
sparkles "^1.0.0"
-gonzales-pe@^4.0.3:
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.2.3.tgz#41091703625433285e0aee3aa47829fc1fbeb6f2"
- dependencies:
- minimist "1.1.x"
-
graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@@ -5293,12 +5340,13 @@ growly@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
-gulp-autoprefixer@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/gulp-autoprefixer/-/gulp-autoprefixer-4.0.0.tgz#e00a8c571b85d06516ac26341be90dfd9fc1eab0"
+gulp-autoprefixer@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/gulp-autoprefixer/-/gulp-autoprefixer-5.0.0.tgz#8237c278a69775270a1cafe7d6f101cfcd585544"
dependencies:
- autoprefixer "^7.0.0"
- gulp-util "^3.0.0"
+ autoprefixer "^8.0.0"
+ fancy-log "^1.3.2"
+ plugin-error "^1.0.1"
postcss "^6.0.1"
through2 "^2.0.0"
vinyl-sourcemaps-apply "^0.2.0"
@@ -5421,17 +5469,18 @@ gulp-stylefmt@^1.1.0:
stylefmt "^5.0.4"
through2 "^2.0.1"
-gulp-stylelint@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/gulp-stylelint/-/gulp-stylelint-4.0.0.tgz#440fa7e6c447e92644700e1e2a06a73e6e457750"
+gulp-stylelint@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/gulp-stylelint/-/gulp-stylelint-7.0.0.tgz#4249dc36a983e6a2c28a449f73d9a369bb14b458"
dependencies:
- chalk "^2.0.1"
+ chalk "^2.3.0"
deep-extend "^0.5.0"
- gulp-util "^3.0.8"
+ fancy-log "^1.3.2"
mkdirp "^0.5.1"
+ plugin-error "^1.0.1"
promise "^8.0.1"
+ source-map "^0.5.6"
strip-ansi "^4.0.0"
- stylelint "^8.0.0"
through2 "^2.0.3"
gulp-uglify-es@^1.0.1:
@@ -5601,6 +5650,10 @@ has-flag@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
+has-flag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+
has-gulplog@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce"
@@ -5781,7 +5834,7 @@ htmlescape@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351"
-htmlparser2@^3.9.1, htmlparser2@^3.9.2:
+htmlparser2@^3.9.1:
version "3.9.2"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
dependencies:
@@ -5916,7 +5969,7 @@ iframe@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/iframe/-/iframe-1.0.0.tgz#58e74822b178a0579d09cd169640fb9537470ef5"
-ignore@^3.2.0, ignore@^3.3.3, ignore@^3.3.5:
+ignore@^3.2.0, ignore@^3.3.3:
version "3.3.7"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021"
@@ -5942,10 +5995,6 @@ indent-string@^2.1.0:
dependencies:
repeating "^2.0.0"
-indent-string@^3.0.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
-
indexes-of@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
@@ -6082,10 +6131,6 @@ is-alphabetical@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.1.tgz#c77079cc91d4efac775be1034bf2d243f95e6f08"
-is-alphanumeric@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz#4a9cef71daf4c001c1d81d63d140cf53fd6889f4"
-
is-alphanumerical@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.1.tgz#dfb4aa4d1085e33bdb61c2dee9c80e9c6c19f53b"
@@ -6276,10 +6321,6 @@ is-number@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
-is-obj@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
-
is-odd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-1.0.0.tgz#3b8a932eb028b3775c39bb09e91767accdb69088"
@@ -6406,14 +6447,14 @@ is-windows@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c"
+is-windows@^1.0.0, is-windows@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+
is-windows@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.1.tgz#310db70f742d259a16a369202b51af84233310d9"
-is-windows@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
-
is-word-character@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.1.tgz#5a03fa1ea91ace8a6eb0c7cd770eb86d65c8befb"
@@ -6553,7 +6594,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
-js-yaml@^3.2.5, js-yaml@^3.2.7, js-yaml@^3.4.3, js-yaml@^3.6.1, js-yaml@^3.9.0, js-yaml@^3.9.1:
+js-yaml@^3.2.5, js-yaml@^3.2.7, js-yaml@^3.4.3, js-yaml@^3.6.1, js-yaml@^3.9.1:
version "3.10.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc"
dependencies:
@@ -6620,10 +6661,6 @@ json-loader@^0.5.4:
version "0.5.7"
resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d"
-json-parse-better-errors@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz#50183cd1b2d25275de069e9e71b467ac9eab973a"
-
json-rpc-engine@^3.0.1, json-rpc-engine@^3.1.0, json-rpc-engine@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.4.0.tgz#8a1647a7f2cc7018f4802f41ec8208d281f78bfc"
@@ -6872,10 +6909,6 @@ known-css-properties@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.2.0.tgz#899c94be368e55b42d7db8d5be7d73a4a4a41454"
-known-css-properties@^0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.5.0.tgz#6ff66943ed4a5b55657ee095779a91f4536f8084"
-
labeled-stream-splicer@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz#a52e1d138024c00b86b1c0c91f677918b8ae0a59"
@@ -7038,15 +7071,6 @@ load-json-file@^1.0.0:
pinkie-promise "^2.0.0"
strip-bom "^2.0.0"
-load-json-file@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
- dependencies:
- graceful-fs "^4.1.2"
- parse-json "^4.0.0"
- pify "^3.0.0"
- strip-bom "^3.0.0"
-
loader-runner@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2"
@@ -7275,12 +7299,6 @@ log-symbols@^1.0.0, log-symbols@^1.0.2:
dependencies:
chalk "^1.0.0"
-log-symbols@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.1.0.tgz#f35fa60e278832b538dc4dddcbb478a45d3e3be6"
- dependencies:
- chalk "^2.0.1"
-
log4js@^2.3.9:
version "2.5.3"
resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.5.3.tgz#38bb7bde5e9c1c181bd75e8bc128c5cd0409caf1"
@@ -7320,10 +7338,6 @@ lolex@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.3.1.tgz#3d2319894471ea0950ef64692ead2a5318cff362"
-longest-streak@^2.0.1:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.2.tgz#2421b6ba939a443bb9ffebf596585a50b4c38e2e"
-
longest@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
@@ -7421,10 +7435,6 @@ map-obj@^1.0.0, map-obj@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
-map-obj@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9"
-
map-stream@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194"
@@ -7439,10 +7449,6 @@ markdown-escapes@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.1.tgz#1994df2d3af4811de59a6714934c2b2292734518"
-markdown-table@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.1.tgz#4b3dd3a133d1518b8ef0dbc709bf2a1b4824bc8c"
-
matchdep@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e"
@@ -7458,7 +7464,7 @@ matcher-collection@^1.0.0:
dependencies:
minimatch "^3.0.2"
-mathml-tag-names@^2.0.0, mathml-tag-names@^2.0.1:
+mathml-tag-names@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.0.1.tgz#8d41268168bf86d1102b98109e28e531e7a34578"
@@ -7479,13 +7485,6 @@ md5.js@^1.3.4:
hash-base "^3.0.0"
inherits "^2.0.1"
-mdast-util-compact@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-1.0.1.tgz#cdb5f84e2b6a2d3114df33bd05d9cb32e3c4083a"
- dependencies:
- unist-util-modify-children "^1.0.0"
- unist-util-visit "^1.1.0"
-
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -7546,20 +7545,6 @@ meow@^3.3.0, meow@^3.7.0:
redent "^1.0.0"
trim-newlines "^1.0.0"
-meow@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/meow/-/meow-4.0.0.tgz#fd5855dd008db5b92c552082db1c307cba20b29d"
- dependencies:
- camelcase-keys "^4.0.0"
- decamelize-keys "^1.0.0"
- loud-rejection "^1.0.0"
- minimist "^1.1.3"
- minimist-options "^3.0.1"
- normalize-package-data "^2.3.4"
- read-pkg-up "^3.0.0"
- redent "^2.0.0"
- trim-newlines "^2.0.0"
-
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
@@ -7748,21 +7733,10 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
dependencies:
brace-expansion "^1.1.7"
-minimist-options@^3.0.1:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954"
- dependencies:
- arrify "^1.0.1"
- is-plain-obj "^1.1.0"
-
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-minimist@1.1.x:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.3.tgz#3bedfd91a92d39016fcfaa1c681e8faa1a1efda8"
-
minimist@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.1.0.tgz#99df657a52574c21c9057497df742790b2b4c0de"
@@ -7775,21 +7749,6 @@ minimist@~0.0.1, minimist@~0.0.8:
version "0.0.10"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
-mississippi@^1.2.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-1.3.0.tgz#d201583eb12327e3c5c1642a404a9cacf94e34f5"
- dependencies:
- concat-stream "^1.5.0"
- duplexify "^3.4.2"
- end-of-stream "^1.1.0"
- flush-write-stream "^1.0.0"
- from2 "^2.1.0"
- parallel-transform "^1.1.0"
- pump "^1.0.0"
- pumpify "^1.3.3"
- stream-each "^1.1.0"
- through2 "^2.0.0"
-
mixin-deep@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.0.tgz#47a8732ba97799457c8c1eca28f95132d7e8150a"
@@ -7859,6 +7818,26 @@ module-deps@^4.0.8:
through2 "^2.0.0"
xtend "^4.0.0"
+module-deps@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.0.0.tgz#4417b49a4f4d7af79b104186e5389ea99b1dc837"
+ dependencies:
+ JSONStream "^1.0.3"
+ browser-resolve "^1.7.0"
+ cached-path-relative "^1.0.0"
+ concat-stream "~1.6.0"
+ defined "^1.0.0"
+ detective "^5.0.2"
+ duplexer2 "^0.1.2"
+ inherits "^2.0.1"
+ parents "^1.0.0"
+ readable-stream "^2.0.2"
+ resolve "^1.4.0"
+ stream-combiner2 "^1.1.1"
+ subarg "^1.0.0"
+ through2 "^2.0.0"
+ xtend "^4.0.0"
+
ms@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
@@ -8589,14 +8568,6 @@ pako@~1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
-parallel-transform@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06"
- dependencies:
- cyclist "~0.2.2"
- inherits "^2.0.3"
- readable-stream "^2.1.5"
-
parents@^1.0.0, parents@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751"
@@ -8654,19 +8625,6 @@ parse-json@^2.2.0:
dependencies:
error-ex "^1.2.0"
-parse-json@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-3.0.0.tgz#fa6f47b18e23826ead32f263e744d0e1e847fb13"
- dependencies:
- error-ex "^1.3.1"
-
-parse-json@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
- dependencies:
- error-ex "^1.3.1"
- json-parse-better-errors "^1.0.1"
-
parse-passwd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
@@ -8775,12 +8733,6 @@ path-type@^1.0.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
-path-type@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
- dependencies:
- pify "^3.0.0"
-
pathval@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0"
@@ -8917,27 +8869,13 @@ post-message-stream@^3.0.0:
dependencies:
readable-stream "^2.1.4"
-postcss-html@^0.12.0:
- version "0.12.0"
- resolved "https://registry.yarnpkg.com/postcss-html/-/postcss-html-0.12.0.tgz#39b6adb4005dfc5464df7999c0f81c95bced7e50"
- dependencies:
- htmlparser2 "^3.9.2"
- remark "^8.0.0"
- unist-util-find-all-after "^1.0.1"
-
postcss-less@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/postcss-less/-/postcss-less-0.14.0.tgz#c631b089c6cce422b9a10f3a958d2bedd3819324"
dependencies:
postcss "^5.0.21"
-postcss-less@^1.1.0:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/postcss-less/-/postcss-less-1.1.3.tgz#6930525271bfe38d5793d33ac09c1a546b87bb51"
- dependencies:
- postcss "^5.2.16"
-
-postcss-media-query-parser@^0.2.0, postcss-media-query-parser@^0.2.3:
+postcss-media-query-parser@^0.2.0:
version "0.2.3"
resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244"
@@ -8959,44 +8897,16 @@ postcss-reporter@^3.0.0:
log-symbols "^1.0.2"
postcss "^5.0.0"
-postcss-reporter@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-5.0.0.tgz#a14177fd1342829d291653f2786efd67110332c3"
- dependencies:
- chalk "^2.0.1"
- lodash "^4.17.4"
- log-symbols "^2.0.0"
- postcss "^6.0.8"
-
postcss-resolve-nested-selector@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e"
-postcss-safe-parser@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-3.0.1.tgz#b753eff6c7c0aea5e8375fbe4cde8bf9063ff142"
- dependencies:
- postcss "^6.0.6"
-
-postcss-sass@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/postcss-sass/-/postcss-sass-0.2.0.tgz#e55516441e9526ba4b380a730d3a02e9eaa78c7a"
- dependencies:
- gonzales-pe "^4.0.3"
- postcss "^6.0.6"
-
postcss-scss@^0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-0.4.1.tgz#ad771b81f0f72f5f4845d08aa60f93557653d54c"
dependencies:
postcss "^5.2.13"
-postcss-scss@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-1.0.2.tgz#ff45cf3354b879ee89a4eb68680f46ac9bb14f94"
- dependencies:
- postcss "^6.0.3"
-
postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.1.1:
version "2.2.3"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90"
@@ -9005,14 +8915,6 @@ postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.1.1:
indexes-of "^1.0.1"
uniq "^1.0.1"
-postcss-selector-parser@^3.1.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865"
- dependencies:
- dot-prop "^4.1.1"
- indexes-of "^1.0.1"
- uniq "^1.0.1"
-
postcss-sorting@^2.0.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/postcss-sorting/-/postcss-sorting-2.1.0.tgz#32b1e9afa913bb225a6ad076d503d8f983bb4a82"
@@ -9033,7 +8935,7 @@ postcss@^5.0.0, postcss@^5.0.18, postcss@^5.0.20, postcss@^5.0.21, postcss@^5.0.
source-map "^0.5.6"
supports-color "^3.2.3"
-postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.3, postcss@^6.0.6, postcss@^6.0.8:
+postcss@^6.0.1:
version "6.0.14"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.14.tgz#5534c72114739e75d0afcf017db853099f562885"
dependencies:
@@ -9041,6 +8943,14 @@ postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.3, postcss@^6.0.6, postcss@^6.0.8:
source-map "^0.6.1"
supports-color "^4.4.0"
+postcss@^6.0.19:
+ version "6.0.19"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.19.tgz#76a78386f670b9d9494a655bf23ac012effd1555"
+ dependencies:
+ chalk "^2.3.1"
+ source-map "^0.6.1"
+ supports-color "^5.2.0"
+
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@@ -9182,7 +9092,14 @@ pump@^1.0.0, pump@^1.0.2:
end-of-stream "^1.1.0"
once "^1.3.1"
-pumpify@^1.3.3, pumpify@^1.3.4, pumpify@^1.3.5:
+pump@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
+ dependencies:
+ end-of-stream "^1.1.0"
+ once "^1.3.1"
+
+pumpify@^1.3.4, pumpify@^1.3.5:
version "1.3.5"
resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.3.5.tgz#1b671c619940abcaeac0ad0e3a3c164be760993b"
dependencies:
@@ -9250,10 +9167,6 @@ querystring@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
-quick-lru@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8"
-
qunitjs@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/qunitjs/-/qunitjs-2.4.1.tgz#88aba055a9e2ec3dbebfaad02471b2cb002c530b"
@@ -9535,13 +9448,6 @@ read-pkg-up@^1.0.1:
find-up "^1.0.0"
read-pkg "^1.0.0"
-read-pkg-up@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07"
- dependencies:
- find-up "^2.0.0"
- read-pkg "^3.0.0"
-
read-pkg@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
@@ -9550,14 +9456,6 @@ read-pkg@^1.0.0:
normalize-package-data "^2.3.2"
path-type "^1.0.0"
-read-pkg@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
- dependencies:
- load-json-file "^4.0.0"
- normalize-package-data "^2.3.2"
- path-type "^3.0.0"
-
read@1.0.x:
version "1.0.7"
resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4"
@@ -9663,13 +9561,6 @@ redent@^1.0.0:
indent-string "^2.1.0"
strip-indent "^1.0.1"
-redent@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa"
- dependencies:
- indent-string "^3.0.0"
- strip-indent "^2.0.0"
-
redis-commands@^1.2.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.3.5.tgz#4495889414f1e886261180b1442e7295602d83a2"
@@ -9779,33 +9670,6 @@ remark-parse@^4.0.0:
vfile-location "^2.0.0"
xtend "^4.0.1"
-remark-stringify@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-4.0.0.tgz#4431884c0418f112da44991b4e356cfe37facd87"
- dependencies:
- ccount "^1.0.0"
- is-alphanumeric "^1.0.0"
- is-decimal "^1.0.0"
- is-whitespace-character "^1.0.0"
- longest-streak "^2.0.1"
- markdown-escapes "^1.0.0"
- markdown-table "^1.1.0"
- mdast-util-compact "^1.0.0"
- parse-entities "^1.0.2"
- repeat-string "^1.5.4"
- state-toggle "^1.0.0"
- stringify-entities "^1.0.1"
- unherit "^1.0.4"
- xtend "^4.0.1"
-
-remark@^8.0.0:
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/remark/-/remark-8.0.0.tgz#287b6df2fe1190e263c1d15e486d3fa835594d6d"
- dependencies:
- remark-parse "^4.0.0"
- remark-stringify "^4.0.0"
- unified "^6.0.0"
-
remove-bom-buffer@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53"
@@ -10022,10 +9886,6 @@ require-from-string@^1.1.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418"
-require-from-string@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.1.tgz#c545233e9d7da6616e9d59adfb39fc9f588676ff"
-
require-main-filename@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
@@ -10067,10 +9927,6 @@ resolve-from@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
-resolve-from@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
-
resolve-options@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131"
@@ -10707,7 +10563,7 @@ spdx-license-ids@^1.0.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57"
-specificity@^0.3.0, specificity@^0.3.1:
+specificity@^0.3.0:
version "0.3.2"
resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.3.2.tgz#99e6511eceef0f8d9b57924937aac2cb13d13c42"
@@ -10833,13 +10689,6 @@ stream-combiner@~0.0.4:
dependencies:
duplexer "~0.1.1"
-stream-each@^1.1.0:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.2.tgz#8e8c463f91da8991778765873fe4d960d8f616bd"
- dependencies:
- end-of-stream "^1.1.0"
- stream-shift "^1.0.0"
-
stream-exhaust@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/stream-exhaust/-/stream-exhaust-1.0.2.tgz#acdac8da59ef2bc1e17a2c0ccf6c320d120e555d"
@@ -10924,15 +10773,6 @@ string_decoder@~0.10.x:
version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
-stringify-entities@^1.0.1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-1.3.1.tgz#b150ec2d72ac4c1b5f324b51fb6b28c9cdff058c"
- dependencies:
- character-entities-html4 "^1.0.0"
- character-entities-legacy "^1.0.0"
- is-alphanumerical "^1.0.0"
- is-hexadecimal "^1.0.0"
-
stringstream@~0.0.4, stringstream@~0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
@@ -10972,10 +10812,6 @@ strip-bom@^2.0.0:
dependencies:
is-utf8 "^0.2.0"
-strip-bom@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
-
strip-eof@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
@@ -10992,10 +10828,6 @@ strip-indent@^1.0.1:
dependencies:
get-stdin "^4.0.1"
-strip-indent@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
-
strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@@ -11042,15 +10874,15 @@ stylehacks@^2.3.2:
text-table "^0.2.0"
write-file-stdout "0.0.2"
-stylelint-config-recommended@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-1.0.0.tgz#752c17fc68fa64cd5e7589e24f6e46e77e14a735"
+stylelint-config-recommended@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-2.1.0.tgz#f526d5c771c6811186d9eaedbed02195fee30858"
-stylelint-config-standard@^17.0.0:
- version "17.0.0"
- resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-17.0.0.tgz#42103a090054ee2a3dde9ecaed55e5d4d9d059fc"
+stylelint-config-standard@^18.2.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-18.2.0.tgz#6283149aba7f64f18731aef8f0abfb35cf619e06"
dependencies:
- stylelint-config-recommended "^1.0.0"
+ stylelint-config-recommended "^2.1.0"
stylelint-order@0.4.x:
version "0.4.4"
@@ -11104,50 +10936,6 @@ stylelint@^7.5.0, stylelint@^7.9.0:
svg-tags "^1.0.0"
table "^4.0.1"
-stylelint@^8.0.0:
- version "8.4.0"
- resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-8.4.0.tgz#c2dbaeb17236917819f9206e1c0df5fddf6f83c3"
- dependencies:
- autoprefixer "^7.1.2"
- balanced-match "^1.0.0"
- chalk "^2.0.1"
- cosmiconfig "^3.1.0"
- debug "^3.0.0"
- execall "^1.0.0"
- file-entry-cache "^2.0.0"
- get-stdin "^5.0.1"
- globby "^7.0.0"
- globjoin "^0.1.4"
- html-tags "^2.0.0"
- ignore "^3.3.3"
- imurmurhash "^0.1.4"
- known-css-properties "^0.5.0"
- lodash "^4.17.4"
- log-symbols "^2.0.0"
- mathml-tag-names "^2.0.1"
- meow "^4.0.0"
- micromatch "^2.3.11"
- normalize-selector "^0.2.0"
- pify "^3.0.0"
- postcss "^6.0.6"
- postcss-html "^0.12.0"
- postcss-less "^1.1.0"
- postcss-media-query-parser "^0.2.3"
- postcss-reporter "^5.0.0"
- postcss-resolve-nested-selector "^0.1.1"
- postcss-safe-parser "^3.0.1"
- postcss-sass "^0.2.0"
- postcss-scss "^1.0.2"
- postcss-selector-parser "^3.1.0"
- postcss-value-parser "^3.3.0"
- resolve-from "^4.0.0"
- specificity "^0.3.1"
- string-width "^2.1.0"
- style-search "^0.1.0"
- sugarss "^1.0.0"
- svg-tags "^1.0.0"
- table "^4.0.1"
-
subarg@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2"
@@ -11160,12 +10948,6 @@ sugarss@^0.2.0:
dependencies:
postcss "^5.2.4"
-sugarss@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-1.0.1.tgz#be826d9003e0f247735f92365dc3fd7f1bae9e44"
- dependencies:
- postcss "^6.0.14"
-
supports-color@4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e"
@@ -11192,6 +10974,12 @@ supports-color@^4.0.0, supports-color@^4.4.0:
dependencies:
has-flag "^2.0.0"
+supports-color@^5.2.0, supports-color@^5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0"
+ dependencies:
+ has-flag "^3.0.0"
+
sver-compat@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8"
@@ -11542,10 +11330,6 @@ trim-newlines@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
-trim-newlines@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20"
-
trim-right@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
@@ -11587,6 +11371,10 @@ tty-browserify@0.0.0, tty-browserify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
+tty-browserify@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811"
+
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
@@ -11735,7 +11523,7 @@ unherit@^1.0.4:
inherits "^2.0.1"
xtend "^4.0.1"
-unified@^6.0.0, unified@^6.1.5:
+unified@^6.1.5:
version "6.1.6"
resolved "https://registry.yarnpkg.com/unified/-/unified-6.1.6.tgz#5ea7f807a0898f1f8acdeefe5f25faa010cc42b1"
dependencies:
@@ -11767,22 +11555,10 @@ unique-stream@^2.0.2:
json-stable-stringify "^1.0.0"
through2-filter "^2.0.0"
-unist-util-find-all-after@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/unist-util-find-all-after/-/unist-util-find-all-after-1.0.1.tgz#4e5512abfef7e0616781aecf7b1ed751c00af908"
- dependencies:
- unist-util-is "^2.0.0"
-
-unist-util-is@^2.0.0, unist-util-is@^2.1.1:
+unist-util-is@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-2.1.1.tgz#0c312629e3f960c66e931e812d3d80e77010947b"
-unist-util-modify-children@^1.0.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/unist-util-modify-children/-/unist-util-modify-children-1.1.1.tgz#66d7e6a449e6f67220b976ab3cb8b5ebac39e51d"
- dependencies:
- array-iterate "^1.0.0"
-
unist-util-remove-position@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.1.tgz#5a85c1555fc1ba0c101b86707d15e50fa4c871bb"