mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
commit
c2f97717c0
24
CHANGELOG.md
24
CHANGELOG.md
@ -2,18 +2,36 @@
|
||||
|
||||
## Current Develop Branch
|
||||
|
||||
## 4.16.0 Wednesday October 17 2018
|
||||
|
||||
- Feature: Add toggle for primary currency (eth/fiat)
|
||||
- Feature: add tooltip for view etherscan tx
|
||||
- Feature: add Polish translations
|
||||
- Feature: improve Korean translations
|
||||
- Feature: improve Italian translations
|
||||
- Bug Fix: Fix bug with "pending" block reference
|
||||
- Bug Fix: Force AccountTracker to update balances on network change
|
||||
- Bug Fix: Fix document extension check when injecting web3
|
||||
- Bug Fix: Fix some support links
|
||||
|
||||
## 4.15.0 Thursday October 11 2018
|
||||
|
||||
- A rollback release, equivalent to `v4.11.1` to be deployed in the case that `v4.14.0` is found to have bugs.
|
||||
|
||||
## 4.14.0 Thursday October 11 2018
|
||||
|
||||
- Update transaction statuses when switching networks.
|
||||
- [#5470](https://github.com/MetaMask/metamask-extension/pull/5470) 100% coverage in French locale, fixed the procedure to verify proposed locale.
|
||||
- Added rudimentary support for the subscription API to support web3 1.0 and Truffle's Drizzle.
|
||||
- [#5502](https://github.com/MetaMask/metamask-extension/pull/5502) Update Italian translation.
|
||||
|
||||
## 4.13.0
|
||||
|
||||
- A rollback release, equivalent to `v4.11.1` to be deployed in the case that `v4.12.0` is found to have bugs.
|
||||
|
||||
## 4.12.0 Thursday September 27 2018
|
||||
|
||||
- Reintroduces changes from 4.10.0
|
||||
## 4.13.0
|
||||
|
||||
- A rollback release, equivalent to `v4.11.1` to be deployed in the case that `v4.12.0` is found to have bugs.
|
||||
|
||||
## 4.11.1 Tuesday September 25 2018
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# MetaMask Browser Extension
|
||||
[](https://circleci.com/gh/MetaMask/metamask-extension) [](https://coveralls.io/github/MetaMask/metamask-extension?branch=master) [](https://greenkeeper.io/) [](https://waffle.io/MetaMask/metamask-extension)
|
||||
[](https://circleci.com/gh/MetaMask/metamask-extension) [](https://coveralls.io/github/MetaMask/metamask-extension?branch=master) [](https://waffle.io/MetaMask/metamask-extension)
|
||||
|
||||
## Support
|
||||
|
||||
|
@ -140,6 +140,9 @@
|
||||
"clickCopy": {
|
||||
"message": "Click to Copy"
|
||||
},
|
||||
"clickToAdd": {
|
||||
"message": "Click on $1 to add them to your account"
|
||||
},
|
||||
"close": {
|
||||
"message": "Close"
|
||||
},
|
||||
@ -361,6 +364,9 @@
|
||||
"enterPasswordContinue": {
|
||||
"message": "Enter password to continue"
|
||||
},
|
||||
"eth": {
|
||||
"message": "ETH"
|
||||
},
|
||||
"etherscanView": {
|
||||
"message": "View account on Etherscan"
|
||||
},
|
||||
@ -380,7 +386,7 @@
|
||||
"message": "Failed"
|
||||
},
|
||||
"fiat": {
|
||||
"message": "FIAT",
|
||||
"message": "Fiat",
|
||||
"description": "Exchange type"
|
||||
},
|
||||
"fileImportFail": {
|
||||
@ -424,6 +430,9 @@
|
||||
"gasLimitTooLow": {
|
||||
"message": "Gas limit must be at least 21000"
|
||||
},
|
||||
"gasUsed": {
|
||||
"message": "Gas Used"
|
||||
},
|
||||
"generatingSeed": {
|
||||
"message": "Generating Seed..."
|
||||
},
|
||||
@ -635,6 +644,9 @@
|
||||
"min": {
|
||||
"message": "Minimum"
|
||||
},
|
||||
"missingYourTokens": {
|
||||
"message": "Don't see your tokens?"
|
||||
},
|
||||
"myAccounts": {
|
||||
"message": "My Accounts"
|
||||
},
|
||||
@ -787,6 +799,12 @@
|
||||
"prev": {
|
||||
"message": "Prev"
|
||||
},
|
||||
"primaryCurrencySetting": {
|
||||
"message": "Primary Currency"
|
||||
},
|
||||
"primaryCurrencySettingDescription": {
|
||||
"message": "Select ETH to prioritize displaying values in ETH. Select Fiat to prioritize displaying values in your selected currency."
|
||||
},
|
||||
"privacyMsg": {
|
||||
"message": "Privacy Policy"
|
||||
},
|
||||
@ -795,7 +813,7 @@
|
||||
"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."
|
||||
"message": "Warning: Never disclose this key. Anyone with your private keys can steal any assets held in your account."
|
||||
},
|
||||
"privateNetwork": {
|
||||
"message": "Private Network"
|
||||
@ -1186,7 +1204,7 @@
|
||||
"message": "These 12 words are the only way to restore your MetaMask accounts.\nSave them somewhere safe and secret."
|
||||
},
|
||||
"typePassword": {
|
||||
"message": "Type Your Password"
|
||||
"message": "Type your MetaMask password"
|
||||
},
|
||||
"uiWelcome": {
|
||||
"message": "Welcome to the New UI (Beta)"
|
||||
|
@ -156,7 +156,7 @@
|
||||
"message": " Copiar "
|
||||
},
|
||||
"copyPrivateKey": {
|
||||
"message": "Ésta es tu llave privada (haz click para copiar)"
|
||||
"message": "Ésta es tu clave privada (haz click para copiar)"
|
||||
},
|
||||
"copyToClipboard": {
|
||||
"message": "Copiar al portapapeles"
|
||||
@ -278,10 +278,10 @@
|
||||
"message": "Tipo de cambio"
|
||||
},
|
||||
"exportPrivateKey": {
|
||||
"message": "Exportar llave privada"
|
||||
"message": "Exportar clave privada"
|
||||
},
|
||||
"exportPrivateKeyWarning": {
|
||||
"message": "Exportar llaves privadas bajo TU PROPIO riesgo"
|
||||
"message": "Exportar claves privadas bajo TU PROPIO riesgo"
|
||||
},
|
||||
"failed": {
|
||||
"message": "Fallo"
|
||||
@ -579,8 +579,8 @@
|
||||
"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"
|
||||
"message": "Pega tu clave privada aqui",
|
||||
"description": "Para importar una cuenta desde una clave privada"
|
||||
},
|
||||
"pasteSeed": {
|
||||
"message": "¡Pega tu frase semilla aquí!"
|
||||
@ -595,7 +595,7 @@
|
||||
"message": "Política de privacidad"
|
||||
},
|
||||
"privateKey": {
|
||||
"message": "Llave privada",
|
||||
"message": "Clave privada",
|
||||
"description": "Selecciona este tupo de archivo para importar una cuenta"
|
||||
},
|
||||
"privateKeyWarning": {
|
||||
@ -712,7 +712,7 @@
|
||||
"message": "Comprar con ShapeShift"
|
||||
},
|
||||
"showPrivateKeys": {
|
||||
"message": "Mostrar llaves privadas"
|
||||
"message": "Mostrar claves privadas"
|
||||
},
|
||||
"showQRCode": {
|
||||
"message": "Mostrar codigo QR"
|
||||
|
@ -11,6 +11,7 @@
|
||||
{ "code": "ko", "name": "Korean" },
|
||||
{ "code": "nl", "name": "Dutch" },
|
||||
{ "code": "ph", "name": "Tagalog" },
|
||||
{ "code": "pl", "name": "Polish" },
|
||||
{ "code": "pt", "name": "Portuguese" },
|
||||
{ "code": "ru", "name": "Russian" },
|
||||
{ "code": "sl", "name": "Slovenian" },
|
||||
|
@ -2,6 +2,9 @@
|
||||
"accept": {
|
||||
"message": "Accetta"
|
||||
},
|
||||
"accessingYourCamera": {
|
||||
"message": "Accesso alla fotocamera..."
|
||||
},
|
||||
"account": {
|
||||
"message": "Account"
|
||||
},
|
||||
@ -11,6 +14,15 @@
|
||||
"accountName": {
|
||||
"message": "Nome Account"
|
||||
},
|
||||
"accountOptions": {
|
||||
"message": "Account Options"
|
||||
},
|
||||
"accountSelectionRequired": {
|
||||
"message": "Devi selezionare un account!"
|
||||
},
|
||||
"activityLog": {
|
||||
"message": "log attività"
|
||||
},
|
||||
"address": {
|
||||
"message": "Indirizzo"
|
||||
},
|
||||
@ -23,6 +35,12 @@
|
||||
"addTokens": {
|
||||
"message": "Aggiungi più token"
|
||||
},
|
||||
"addSuggestedTokens": {
|
||||
"message": "Aggiungi Token Suggeriti"
|
||||
},
|
||||
"addAcquiredTokens": {
|
||||
"message": "Aggiungi i token che hai acquistato usando MetaMask"
|
||||
},
|
||||
"amount": {
|
||||
"message": "Importo"
|
||||
},
|
||||
@ -37,9 +55,21 @@
|
||||
"message": "MetaMask",
|
||||
"description": "Il nome dell'applicazione"
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approva"
|
||||
},
|
||||
"approved": {
|
||||
"message": "Approvato"
|
||||
},
|
||||
"attemptingConnect": {
|
||||
"message": "Tentativo di connessione alla blockchain."
|
||||
},
|
||||
"attemptToCancel": {
|
||||
"message": "Tentativo di Annullamento?"
|
||||
},
|
||||
"attemptToCancelDescription": {
|
||||
"message": "Tentare di annullare non garantisce che la transazione verrà annullata. Se annullata, è comunque richiesto pagare alla rete la commissione sulla transazione."
|
||||
},
|
||||
"attributions": {
|
||||
"message": "Attribuzioni"
|
||||
},
|
||||
@ -71,8 +101,11 @@
|
||||
"borrowDharma": {
|
||||
"message": "Prendi in presisito con Dharma (Beta)"
|
||||
},
|
||||
"browserNotSupported": {
|
||||
"message": "Il tuo Browser non è supportato..."
|
||||
},
|
||||
"builtInCalifornia": {
|
||||
"message": "MetaMask è progettato e costruito in California."
|
||||
"message": "MetaMask è progettato e realizzato in California."
|
||||
},
|
||||
"buy": {
|
||||
"message": "Compra"
|
||||
@ -83,8 +116,23 @@
|
||||
"buyCoinbaseExplainer": {
|
||||
"message": "Coinbase è il servizio più popolare al mondo per comprare e vendere Bitcoin, Ethereum e Litecoin."
|
||||
},
|
||||
"bytes": {
|
||||
"message": "Bytes"
|
||||
},
|
||||
"ok": {
|
||||
"message": "Ok"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "Cancella"
|
||||
"message": "Annulla"
|
||||
},
|
||||
"cancelAttempt": {
|
||||
"message": "Tentativo di Annullamento"
|
||||
},
|
||||
"cancellationGasFee": {
|
||||
"message": "Commissione di Annullamento in Gas"
|
||||
},
|
||||
"cancelN": {
|
||||
"message": "Cancel all $1 transactions"
|
||||
},
|
||||
"classicInterface": {
|
||||
"message": "Usa l'interfaccia classica"
|
||||
@ -92,9 +140,18 @@
|
||||
"clickCopy": {
|
||||
"message": "Clicca per Copiare"
|
||||
},
|
||||
"close": {
|
||||
"message": "Chiudi"
|
||||
},
|
||||
"chromeRequiredForHardwareWallets": {
|
||||
"message": "Devi usare MetaMask con Google Chrome per connettere il tuo Portafoglio Hardware"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Conferma"
|
||||
},
|
||||
"confirmed": {
|
||||
"message": "Confermata"
|
||||
},
|
||||
"confirmContract": {
|
||||
"message": "Conferma Contratto"
|
||||
},
|
||||
@ -104,6 +161,36 @@
|
||||
"confirmTransaction": {
|
||||
"message": "Conferma Transazione"
|
||||
},
|
||||
"connectHardwareWallet": {
|
||||
"message": "Connetti Portafoglio Hardware"
|
||||
},
|
||||
"connect": {
|
||||
"message": "Connetti"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Connessione..."
|
||||
},
|
||||
"connectingToKovan": {
|
||||
"message": "Connessione alla Rete di test Kovan"
|
||||
},
|
||||
"connectingToMainnet": {
|
||||
"message": "Connessione alla Rete Ethereum Principale"
|
||||
},
|
||||
"connectingToRopsten": {
|
||||
"message": "Connessione alla Rete di test Ropsten"
|
||||
},
|
||||
"connectingToRinkeby": {
|
||||
"message": "Connessione alla Rete di test Rinkeby"
|
||||
},
|
||||
"connectingToUnknown": {
|
||||
"message": "Connessione ad una Rete Sconosciuta"
|
||||
},
|
||||
"connectToLedger": {
|
||||
"message": "Connettersi al Ledger"
|
||||
},
|
||||
"connectToTrezor": {
|
||||
"message": "Connettersi al Trezor"
|
||||
},
|
||||
"continue": {
|
||||
"message": "Continua"
|
||||
},
|
||||
@ -131,6 +218,9 @@
|
||||
"copy": {
|
||||
"message": "Copia"
|
||||
},
|
||||
"copyAddress": {
|
||||
"message": "Copia l'indirizzo"
|
||||
},
|
||||
"copyToClipboard": {
|
||||
"message": "Copia negli appunti"
|
||||
},
|
||||
@ -156,12 +246,21 @@
|
||||
"currentConversion": {
|
||||
"message": "Cambio Corrente"
|
||||
},
|
||||
"currentLanguage": {
|
||||
"message": "Lingua Corrente"
|
||||
},
|
||||
"currentNetwork": {
|
||||
"message": "Rete Corrente"
|
||||
},
|
||||
"currentRpc": {
|
||||
"message": "RPC Corrente"
|
||||
},
|
||||
"customGas": {
|
||||
"message": "Personalizza Gas"
|
||||
},
|
||||
"customToken": {
|
||||
"message": "Token Personalizzato"
|
||||
},
|
||||
"customize": {
|
||||
"message": "Personalizza"
|
||||
},
|
||||
@ -223,33 +322,54 @@
|
||||
"done": {
|
||||
"message": "Finito"
|
||||
},
|
||||
"downloadGoogleChrome": {
|
||||
"message": "Scarica Google Chrome"
|
||||
},
|
||||
"downloadStateLogs": {
|
||||
"message": "Scarica i log di Stato"
|
||||
},
|
||||
"dontHaveAHardwareWallet": {
|
||||
"message": "Non hai un portafoglio hardware?"
|
||||
},
|
||||
"dropped": {
|
||||
"message": "Abbandonata"
|
||||
},
|
||||
"edit": {
|
||||
"message": "Modifica"
|
||||
},
|
||||
"editAccountName": {
|
||||
"message": "Modifica Nome Account"
|
||||
},
|
||||
"editingTransaction": {
|
||||
"message": "Modifica la transazione"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Mandaci una mail!"
|
||||
},
|
||||
"encryptNewDen": {
|
||||
"message": "Cripta il tuo nuovo DEN"
|
||||
},
|
||||
"ensNameNotFound": {
|
||||
"message": "Nome ENS non trovato"
|
||||
},
|
||||
"enterPassword": {
|
||||
"message": "Inserisci password"
|
||||
},
|
||||
"enterPasswordConfirm": {
|
||||
"message": "Inserisci la tua password per confermare"
|
||||
},
|
||||
"enterPasswordContinue": {
|
||||
"message": "Inserisci la tua password per continuare"
|
||||
},
|
||||
"etherscanView": {
|
||||
"message": "Vedi account su Etherscan"
|
||||
},
|
||||
"exchangeRate": {
|
||||
"message": "Tasso di cambio"
|
||||
},
|
||||
"expandView": {
|
||||
"message": "Expand View"
|
||||
},
|
||||
"exportPrivateKey": {
|
||||
"message": "Esporta Chiave Privata"
|
||||
},
|
||||
@ -257,7 +377,7 @@
|
||||
"message": "Esporta chiave privata a tuo rischio."
|
||||
},
|
||||
"failed": {
|
||||
"message": "Fallito"
|
||||
"message": "Fallita"
|
||||
},
|
||||
"fiat": {
|
||||
"message": "FIAT",
|
||||
@ -270,6 +390,9 @@
|
||||
"followTwitter": {
|
||||
"message": "Seguici su Twitter"
|
||||
},
|
||||
"forgetDevice": {
|
||||
"message": "Dimentica questo dispositivo"
|
||||
},
|
||||
"from": {
|
||||
"message": "Da"
|
||||
},
|
||||
@ -279,6 +402,9 @@
|
||||
"fromShapeShift": {
|
||||
"message": "Da ShapeShift"
|
||||
},
|
||||
"functionType": {
|
||||
"message": "Tipo della Funzione"
|
||||
},
|
||||
"gas": {
|
||||
"message": "Gas",
|
||||
"description": "Piccola indicazione del costo del gas"
|
||||
@ -310,6 +436,9 @@
|
||||
"gasPriceRequired": {
|
||||
"message": "Prezzo Gas Richiesto"
|
||||
},
|
||||
"generatingTransaction": {
|
||||
"message": "Generando la transazione"
|
||||
},
|
||||
"getEther": {
|
||||
"message": "Ottieni Ether"
|
||||
},
|
||||
@ -317,10 +446,28 @@
|
||||
"message": "Ottieni Get Ether da un faucet per $1",
|
||||
"description": "Visualizza il nome della rete per il faucet Ether"
|
||||
},
|
||||
"getHelp": {
|
||||
"message": "Aiuto."
|
||||
},
|
||||
"greaterThanMin": {
|
||||
"message": "deve essere maggiore o uguale a $1.",
|
||||
"description": "aiuto per inserire un input esadecimale come decimale"
|
||||
},
|
||||
"hardware": {
|
||||
"message": "hardware"
|
||||
},
|
||||
"hardwareWalletConnected": {
|
||||
"message": "Portafoglio hardware connesso"
|
||||
},
|
||||
"hardwareWallets": {
|
||||
"message": "Connetti portafoglio hardware"
|
||||
},
|
||||
"hardwareWalletsMsg": {
|
||||
"message": "Selezione un portafoglio hardware che vuoi utilizzare con MetaMask"
|
||||
},
|
||||
"havingTroubleConnecting": {
|
||||
"message": "Problemi di connessione?"
|
||||
},
|
||||
"here": {
|
||||
"message": "qui",
|
||||
"description": "per intendere -clicca qui- per maggiori informazioni (va con troubleTokenBalances)"
|
||||
@ -328,6 +475,9 @@
|
||||
"hereList": {
|
||||
"message": "Questa è una lista!!!!"
|
||||
},
|
||||
"hexData": {
|
||||
"message": "Dati Hex"
|
||||
},
|
||||
"hide": {
|
||||
"message": "Nascondi"
|
||||
},
|
||||
@ -337,6 +487,9 @@
|
||||
"hideTokenPrompt": {
|
||||
"message": "Nascondi Token?"
|
||||
},
|
||||
"history": {
|
||||
"message": "Storico"
|
||||
},
|
||||
"howToDeposit": {
|
||||
"message": "Come vuoi depositare Ether?"
|
||||
},
|
||||
@ -363,9 +516,18 @@
|
||||
"message": "Importato",
|
||||
"description": "stato che conferma che un account è stato totalmente caricato nel portachiavi"
|
||||
},
|
||||
"importUsingSeed": {
|
||||
"message": "Importa account con frase seed"
|
||||
},
|
||||
"info": {
|
||||
"message": "Informazioni"
|
||||
},
|
||||
"infoHelp": {
|
||||
"message": "Informazioni & Aiuto"
|
||||
},
|
||||
"initialTransactionConfirmed": {
|
||||
"message": "La transazione iniziale è stata confermata dalla rete. Clicca OK per tornare indietro."
|
||||
},
|
||||
"insufficientFunds": {
|
||||
"message": "Fondi non sufficienti."
|
||||
},
|
||||
@ -390,6 +552,9 @@
|
||||
"invalidRPC": {
|
||||
"message": "URI RPC invalido"
|
||||
},
|
||||
"invalidSeedPhrase": {
|
||||
"message": "Frase seed non valida"
|
||||
},
|
||||
"jsonFail": {
|
||||
"message": "Qualcosa è andato storto. Assicurati che il file JSON sia formattato correttamente."
|
||||
},
|
||||
@ -397,12 +562,24 @@
|
||||
"message": "File JSON",
|
||||
"description": "formato per importare un account"
|
||||
},
|
||||
"keepTrackTokens": {
|
||||
"message": "Tieni traccia dei tokens che hai acquistato con il tuo account MetaMask."
|
||||
},
|
||||
"kovan": {
|
||||
"message": "Rete di test Kovan"
|
||||
},
|
||||
"knowledgeDataBase": {
|
||||
"message": "Visita la nostra Knowledge Base"
|
||||
},
|
||||
"max": {
|
||||
"message": "Massimo"
|
||||
},
|
||||
"learnMore": {
|
||||
"message": "Scopri di più"
|
||||
},
|
||||
"ledgerAccountRestriction": {
|
||||
"message": "E' necessario utilizzare l'ultimo account prima di poterne aggiungere uno nuovo."
|
||||
},
|
||||
"lessThanMax": {
|
||||
"message": "deve essere minore o uguale a $1.",
|
||||
"description": "aiuto per inserire un input esadecimale come decimale"
|
||||
@ -410,6 +587,9 @@
|
||||
"likeToAddTokens": {
|
||||
"message": "Vorresti aggiungere questi token?"
|
||||
},
|
||||
"links": {
|
||||
"message": "Collegamenti"
|
||||
},
|
||||
"limit": {
|
||||
"message": "Limite"
|
||||
},
|
||||
@ -437,17 +617,26 @@
|
||||
"mainnet": {
|
||||
"message": "Rete Ethereum Principale"
|
||||
},
|
||||
"menu": {
|
||||
"message": "Menu"
|
||||
},
|
||||
"message": {
|
||||
"message": "Messaggio"
|
||||
},
|
||||
"metamaskDescription": {
|
||||
"message": "MetaMask è una cassaforte sicura per identità su Ethereum."
|
||||
},
|
||||
"metamaskSeedWords": {
|
||||
"message": "Parole Seed di MetaMask"
|
||||
},
|
||||
"metamaskVersion": {
|
||||
"message": "versione di MetaMask"
|
||||
},
|
||||
"min": {
|
||||
"message": "Minimo"
|
||||
},
|
||||
"myAccounts": {
|
||||
"message": "Account Miei"
|
||||
"message": "Miei Account"
|
||||
},
|
||||
"mustSelectOne": {
|
||||
"message": "Devi selezionare almeno un token."
|
||||
@ -469,6 +658,9 @@
|
||||
"networks": {
|
||||
"message": "Reti"
|
||||
},
|
||||
"nevermind": {
|
||||
"message": "Non importa"
|
||||
},
|
||||
"newAccount": {
|
||||
"message": "Nuovo Account"
|
||||
},
|
||||
@ -482,6 +674,9 @@
|
||||
"newPassword": {
|
||||
"message": "Nuova Password (minimo 8 caratteri)"
|
||||
},
|
||||
"newPassword8Chars": {
|
||||
"message": "Nuova Password (minimo 8 caratteri)"
|
||||
},
|
||||
"newRecipient": {
|
||||
"message": "Nuovo Destinatario"
|
||||
},
|
||||
@ -489,7 +684,7 @@
|
||||
"message": "Nuovo URL RPC"
|
||||
},
|
||||
"next": {
|
||||
"message": "Prossimo"
|
||||
"message": "Avanti"
|
||||
},
|
||||
"noAddressForName": {
|
||||
"message": "Nessun indirizzo è stato impostato per questo nome."
|
||||
@ -497,32 +692,75 @@
|
||||
"noDeposits": {
|
||||
"message": "Nessun deposito ricevuto"
|
||||
},
|
||||
"noConversionRateAvailable": {
|
||||
"message": "Tasso di Conversione non Disponibile"
|
||||
},
|
||||
"noTransactionHistory": {
|
||||
"message": "Nessuna cronologia delle transazioni."
|
||||
},
|
||||
"noTransactions": {
|
||||
"message": "Nessuna Transazione"
|
||||
},
|
||||
"notFound": {
|
||||
"message": "Non Trovata"
|
||||
},
|
||||
"notStarted": {
|
||||
"message": "Non Iniziato"
|
||||
},
|
||||
"noWebcamFoundTitle": {
|
||||
"message": "Webcam non trovata"
|
||||
},
|
||||
"noWebcamFound": {
|
||||
"message": "La webcam del tuo computer non è stata trovata. Per favore riprovaci."
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"onlySendToEtherAddress": {
|
||||
"message": "Invia ETH solamente ad un account Ethereum."
|
||||
},
|
||||
"onlySendTokensToAccountAddress": {
|
||||
"message": "Invia solamente $1 ad un account Ethereum.",
|
||||
"description": "mostra il simbolo del token"
|
||||
},
|
||||
"openInTab": {
|
||||
"message": "Apri in una scheda"
|
||||
},
|
||||
"or": {
|
||||
"message": "o",
|
||||
"description": "scelta tra creare o importare un nuovo account"
|
||||
},
|
||||
"orderOneHere": {
|
||||
"message": "Compra un Trezor o un Ledger e tieni i tuoi soldi al sicuro"
|
||||
},
|
||||
"origin": {
|
||||
"message": "Origine"
|
||||
},
|
||||
"outgoing": {
|
||||
"message": "In Uscita"
|
||||
},
|
||||
"parameters": {
|
||||
"message": "Parametri"
|
||||
},
|
||||
"password": {
|
||||
"message": "Password"
|
||||
},
|
||||
"passwordCorrect": {
|
||||
"message": "Assicurati che la password sia corretta."
|
||||
},
|
||||
"passwordsDontMatch": {
|
||||
"message": "Le Password Non Corrispondonos"
|
||||
},
|
||||
"passwordMismatch": {
|
||||
"message": "le password non corrispondono",
|
||||
"description": "nella creazione della password, le due password all'interno dei campi non corrispondono"
|
||||
},
|
||||
"passwordNotLongEnough": {
|
||||
"message": "Password non abbastanza lunga"
|
||||
},
|
||||
"passwordShort": {
|
||||
"message": "password non sufficientemente lunga",
|
||||
"description": "nella creazione della password, la password non è lunga abbastanza"
|
||||
@ -534,12 +772,21 @@
|
||||
"pasteSeed": {
|
||||
"message": "Incolla la tua frase seed qui!"
|
||||
},
|
||||
"pending": {
|
||||
"message": "in corso"
|
||||
},
|
||||
"personalAddressDetected": {
|
||||
"message": "Rilevato indirizzo personale. Inserisci l'indirizzo del contratto del token."
|
||||
},
|
||||
"pleaseReviewTransaction": {
|
||||
"message": "Ricontrolla la tua transazione."
|
||||
},
|
||||
"popularTokens": {
|
||||
"message": "Tokens Popolari"
|
||||
},
|
||||
"prev": {
|
||||
"message": "Precedente"
|
||||
},
|
||||
"privacyMsg": {
|
||||
"message": "Politica sulla Privacy"
|
||||
},
|
||||
@ -556,6 +803,9 @@
|
||||
"qrCode": {
|
||||
"message": "Mostra Codice QR"
|
||||
},
|
||||
"queue": {
|
||||
"message": "Coda"
|
||||
},
|
||||
"readdToken": {
|
||||
"message": "Puoi aggiungere nuovamente questo token in futuro andando in “Aggiungi token” nel menu delle opzioni del tuo account."
|
||||
},
|
||||
@ -574,36 +824,87 @@
|
||||
"refundAddress": {
|
||||
"message": "Indirizzo di Rimborso"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"rejectAll": {
|
||||
"message": "Reject All"
|
||||
},
|
||||
"rejectTxsN": {
|
||||
"message": "Reject $1 transactions"
|
||||
},
|
||||
"rejectTxsDescription": {
|
||||
"message": "You are about to batch reject $1 transactions."
|
||||
},
|
||||
"rejected": {
|
||||
"message": "Respinta"
|
||||
},
|
||||
"reset": {
|
||||
"message": "Reset"
|
||||
},
|
||||
"resetAccount": {
|
||||
"message": "Resetta Account"
|
||||
},
|
||||
"resetAccountDescription": {
|
||||
"message": "Resettare il tuo account cancellerà lo storico delle transazioni."
|
||||
},
|
||||
"restoreFromSeed": {
|
||||
"message": "Ripristina da una frase seed"
|
||||
},
|
||||
"restoreVault": {
|
||||
"message": "Ripristina Cassaforte"
|
||||
},
|
||||
"restoreAccountWithSeed": {
|
||||
"message": "Ripristina Account con la Frase Seed"
|
||||
},
|
||||
"required": {
|
||||
"message": "Richiesto"
|
||||
},
|
||||
"retryWithMoreGas": {
|
||||
"message": "Riprova con un prezzo del Gas maggiore qui"
|
||||
},
|
||||
"restore": {
|
||||
"message": "Ripristina"
|
||||
},
|
||||
"revealSeedWords": {
|
||||
"message": "Rivela Frase Seed"
|
||||
},
|
||||
"revealSeedWordsTitle": {
|
||||
"message": "Frase Seed"
|
||||
},
|
||||
"revealSeedWordsDescription": {
|
||||
"message": "Se cambierai browser o computer, ti servirà questa frase seed per accedere ai tuoi account. Salvala in un posto sicuro e segreto."
|
||||
},
|
||||
"revealSeedWordsWarningTitle": {
|
||||
"message": "NON CONDIVIDERE questa frase con nessuno!"
|
||||
},
|
||||
"revealSeedWordsWarning": {
|
||||
"message": "Non ripristinare la tua frase seed in pubblico!. Queste parole possono essere usate per rubare il tuo account."
|
||||
},
|
||||
"revert": {
|
||||
"message": "Annulla"
|
||||
},
|
||||
"remove": {
|
||||
"message": "rimuovi"
|
||||
},
|
||||
"removeAccount": {
|
||||
"message": "Rimuovi account"
|
||||
},
|
||||
"removeAccountDescription": {
|
||||
"message": "Questo account sarà rimosso dal tuo portafoglio. Per favore assicurati che hai la frase seed originale o la chiave privata per questo account importato prima di continuare. Puoi nuovamente importare o creare un account dal menù a tendina. "
|
||||
},
|
||||
"readyToConnect": {
|
||||
"message": "Pronto a Connetterti?"
|
||||
},
|
||||
"rinkeby": {
|
||||
"message": "Rete di test Rinkeby"
|
||||
},
|
||||
"ropsten": {
|
||||
"message": "Rete di test Ropsten"
|
||||
},
|
||||
"rpc": {
|
||||
"message": "RPC Personalizzata"
|
||||
},
|
||||
"sampleAccountName": {
|
||||
"message": "Es: Il mio nuovo account",
|
||||
"description": "Aiuta l'utente a capire il concetto di aggiungere un nome leggibile al loro account"
|
||||
@ -611,6 +912,9 @@
|
||||
"save": {
|
||||
"message": "Salva"
|
||||
},
|
||||
"saveAsCsvFile": {
|
||||
"message": "Salva Come File CSV"
|
||||
},
|
||||
"saveAsFile": {
|
||||
"message": "Salva come File",
|
||||
"description": "Processo per esportare un account"
|
||||
@ -618,9 +922,18 @@
|
||||
"saveSeedAsFile": {
|
||||
"message": "Salva la Frase Seed come File"
|
||||
},
|
||||
"scanInstructions": {
|
||||
"message": "Posizione il codice QR davanti alla fotocamera"
|
||||
},
|
||||
"scanQrCode": {
|
||||
"message": "Scansiona Codice QR"
|
||||
},
|
||||
"search": {
|
||||
"message": "Cerca"
|
||||
},
|
||||
"searchResults": {
|
||||
"message": "Risultati Ricerca"
|
||||
},
|
||||
"secretPhrase": {
|
||||
"message": "Inserisci la tua frase segreta di dodici parole per ripristinare la cassaforte."
|
||||
},
|
||||
@ -633,6 +946,9 @@
|
||||
"selectCurrency": {
|
||||
"message": "Seleziona Moneta"
|
||||
},
|
||||
"selectLocale": {
|
||||
"message": "Selezione Lingua"
|
||||
},
|
||||
"selectService": {
|
||||
"message": "Seleziona Servizio"
|
||||
},
|
||||
@ -648,6 +964,33 @@
|
||||
"sendTokens": {
|
||||
"message": "Invia Tokens"
|
||||
},
|
||||
"sentEther": {
|
||||
"message": "ether inviati"
|
||||
},
|
||||
"sentTokens": {
|
||||
"message": "tokens inviati"
|
||||
},
|
||||
"separateEachWord": {
|
||||
"message": "Separa ogni parola con un solo spazio"
|
||||
},
|
||||
"searchTokens": {
|
||||
"message": "Cerca Tokens"
|
||||
},
|
||||
"selectAnAddress": {
|
||||
"message": "Seleziona un Indirizzo"
|
||||
},
|
||||
"selectAnAccount": {
|
||||
"message": "Seleziona un Account"
|
||||
},
|
||||
"selectAnAccountHelp": {
|
||||
"message": "Selezione l'account da visualizzare in MetaMask"
|
||||
},
|
||||
"selectHdPath": {
|
||||
"message": "Seleziona Percorso HD"
|
||||
},
|
||||
"selectPathHelp": {
|
||||
"message": "Se non vedi il tuo account Ledger esistente di seguito, prova a cambiare il percorso in \"Legacy (MEW / MyCrypto)\""
|
||||
},
|
||||
"sendTokensAnywhere": {
|
||||
"message": "Invia Tokens a chiunque abbia un account Ethereum"
|
||||
},
|
||||
@ -663,9 +1006,21 @@
|
||||
"showQRCode": {
|
||||
"message": "Mostra Codie QR"
|
||||
},
|
||||
"showHexData": {
|
||||
"message": "Mostra Dati Hex"
|
||||
},
|
||||
"showHexDataDescription": {
|
||||
"message": "Seleziona per mostrare il campo dei dati hex nella schermata di invio"
|
||||
},
|
||||
"sign": {
|
||||
"message": "Firma"
|
||||
},
|
||||
"signatureRequest": {
|
||||
"message": "Firma Richiesta"
|
||||
},
|
||||
"signed": {
|
||||
"message": "Firmata"
|
||||
},
|
||||
"signMessage": {
|
||||
"message": "Firma Messaggio"
|
||||
},
|
||||
@ -681,6 +1036,15 @@
|
||||
"spaceBetween": {
|
||||
"message": "ci può essere solo uno spazio tra le parole"
|
||||
},
|
||||
"speedUp": {
|
||||
"message": "velocizza"
|
||||
},
|
||||
"speedUpTitle": {
|
||||
"message": "Velocizza Transazione"
|
||||
},
|
||||
"speedUpSubtitle": {
|
||||
"message": "Aumenta il prezzo del gas per tentare di sovrascrivere e velocizzare la transazione"
|
||||
},
|
||||
"status": {
|
||||
"message": "Stato"
|
||||
},
|
||||
@ -690,9 +1054,33 @@
|
||||
"stateLogsDescription": {
|
||||
"message": "I log di stato contengono i tuoi indirizzi pubblici e le transazioni effettuate."
|
||||
},
|
||||
"stateLogError": {
|
||||
"message": "Errore nel recupero dei log di stato."
|
||||
},
|
||||
"step1HardwareWallet": {
|
||||
"message": "1. Connetti Portafoglio Hardware"
|
||||
},
|
||||
"step1HardwareWalletMsg": {
|
||||
"message": "Connetti il tuo portafoglio hardware al tuo computer."
|
||||
},
|
||||
"step2HardwareWallet": {
|
||||
"message": "2. Seleziona un Account"
|
||||
},
|
||||
"step2HardwareWalletMsg": {
|
||||
"message": "Selezione l'account che vuoi vedere. Puoi selezionarne solo uno alla volta."
|
||||
},
|
||||
"step3HardwareWallet": {
|
||||
"message": "3. Inizia a usare dApps e molto altro ancora!"
|
||||
},
|
||||
"step3HardwareWalletMsg": {
|
||||
"message": "Usa il tuo account hardware come utilizzeresti qualsiasi account Ethereum. Accedi alle dApps, invia Eth, compra e conserva token ERC20 e token non fungibili come CryptoKitties"
|
||||
},
|
||||
"submit": {
|
||||
"message": "Invia"
|
||||
},
|
||||
"submitted": {
|
||||
"message": "Inviata"
|
||||
},
|
||||
"supportCenter": {
|
||||
"message": "Visita il nostro Centro di Supporto"
|
||||
},
|
||||
@ -715,6 +1103,9 @@
|
||||
"message": "$1 a ETH via ShapeShift",
|
||||
"description": "il sistema riempirà il tipo di deposito all'inizio del messaggio"
|
||||
},
|
||||
"token": {
|
||||
"message": "Token"
|
||||
},
|
||||
"tokenAddress": {
|
||||
"message": "Indirizzo Token"
|
||||
},
|
||||
@ -736,22 +1127,61 @@
|
||||
"total": {
|
||||
"message": "Totale"
|
||||
},
|
||||
"transaction": {
|
||||
"message": "transazione"
|
||||
},
|
||||
"transactionConfirmed": {
|
||||
"message": "Transazione confermata il $2."
|
||||
},
|
||||
"transactionCreated": {
|
||||
"message": "Transazione di valore $1 creata il $2."
|
||||
},
|
||||
"transactionWithNonce": {
|
||||
"message": "Transazione $1"
|
||||
},
|
||||
"transactionDropped": {
|
||||
"message": "Transazione abbandonata il $2."
|
||||
},
|
||||
"transactionSubmitted": {
|
||||
"message": "Transazione inviata il $2."
|
||||
},
|
||||
"transactionUpdated": {
|
||||
"message": "Transazione aggiornata il $2."
|
||||
},
|
||||
"transactionUpdatedGas": {
|
||||
"message": "Transazione aggiornata con un prezzo del gas di $1 il $2."
|
||||
},
|
||||
"transactions": {
|
||||
"message": "transazioni"
|
||||
},
|
||||
"transactionError": {
|
||||
"message": "Errore Transazione. Eccceziona generata nel codice del contratto."
|
||||
},
|
||||
"transactionMemo": {
|
||||
"message": "Promemoria Transazione (opzionale)"
|
||||
},
|
||||
"transactionNumber": {
|
||||
"message": "Numero Transazione"
|
||||
},
|
||||
"transfer": {
|
||||
"message": "Trasferisci"
|
||||
},
|
||||
"transferFrom": {
|
||||
"message": "Transfer From"
|
||||
},
|
||||
"transfers": {
|
||||
"message": "Trasferimenti"
|
||||
},
|
||||
"trezorHardwareWallet": {
|
||||
"message": "TREZOR Portafoglio Hardware"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"tryAgain": {
|
||||
"message": "Prova di nuovo"
|
||||
},
|
||||
"twelveWords": {
|
||||
"message": "Queste 12 parole sono l'unico modo per ripristinare i tuoi account MetaMask. \nSalvale in un posto sicuro e segreto."
|
||||
},
|
||||
@ -764,18 +1194,45 @@
|
||||
"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."
|
||||
},
|
||||
"unapproved": {
|
||||
"message": "Non approvata"
|
||||
},
|
||||
"unavailable": {
|
||||
"message": "Non Disponibile"
|
||||
},
|
||||
"units": {
|
||||
"message": "unità"
|
||||
},
|
||||
"unknown": {
|
||||
"message": "Sconosciuto"
|
||||
},
|
||||
"unknownFunction": {
|
||||
"message": "Funzione Sconosciuta"
|
||||
},
|
||||
"unknownNetwork": {
|
||||
"message": "Rete Privata Sconosciuta"
|
||||
},
|
||||
"unknownNetworkId": {
|
||||
"message": "ID rete sconosciuto"
|
||||
},
|
||||
"unknownQrCode": {
|
||||
"message": "Errore: Non siamo riusciti a identificare il codice QR"
|
||||
},
|
||||
"unknownCameraErrorTitle": {
|
||||
"message": "Ooops! Qualcosa è andato storto...."
|
||||
},
|
||||
"unknownCameraError": {
|
||||
"message": "C'è stato un errore nel tentativo di accedere alla fotocamera. Per favore prova di nuovo..."
|
||||
},
|
||||
"unlock": {
|
||||
"message": "Sblocca"
|
||||
},
|
||||
"unlockMessage": {
|
||||
"message": "Il web decentralizzato ti attende"
|
||||
},
|
||||
"updatedWithDate": {
|
||||
"message": "Aggiornata $1"
|
||||
},
|
||||
"uriErrorMsg": {
|
||||
"message": "Gli URI richiedono un prefisso HTTP/HTTPS."
|
||||
},
|
||||
@ -798,22 +1255,40 @@
|
||||
"viewAccount": {
|
||||
"message": "Vedi Account"
|
||||
},
|
||||
"viewOnEtherscan": {
|
||||
"message": "Vedi su Etherscan"
|
||||
},
|
||||
"visitWebSite": {
|
||||
"message": "Visita il nostro sito web"
|
||||
},
|
||||
"walletSeed": {
|
||||
"message": "Seed del Portafoglio"
|
||||
},
|
||||
"warning": {
|
||||
"message": "Attenzione"
|
||||
},
|
||||
"welcomeBack": {
|
||||
"message": "Bentornato!"
|
||||
},
|
||||
"welcomeBeta": {
|
||||
"message": "Benvenuto nella Beta di MetaMask"
|
||||
},
|
||||
"whatsThis": {
|
||||
"message": "Cos'è questo?"
|
||||
},
|
||||
"yesLetsTry": {
|
||||
"message": "Si, proviamo"
|
||||
},
|
||||
"youNeedToAllowCameraAccess": {
|
||||
"message": "Devi consentire l'accesso alla fotocamera per usare questa funzionalità."
|
||||
},
|
||||
"yourSigRequested": {
|
||||
"message": "E' richiesta la tua firma"
|
||||
},
|
||||
"youSign": {
|
||||
"message": "Ti stai connettendo"
|
||||
},
|
||||
"yourPrivateSeedPhrase": {
|
||||
"message": "La tua frase seed privata"
|
||||
}
|
||||
}
|
||||
|
@ -14,9 +14,15 @@
|
||||
"accountName": {
|
||||
"message": "계정 이름"
|
||||
},
|
||||
"accountOptions": {
|
||||
"message": "계정 옵션"
|
||||
},
|
||||
"accountSelectionRequired": {
|
||||
"message": "계정을 선택하셔야 합니다!"
|
||||
},
|
||||
"activityLog": {
|
||||
"message": "활동 로그"
|
||||
},
|
||||
"address": {
|
||||
"message": "주소"
|
||||
},
|
||||
@ -29,6 +35,9 @@
|
||||
"addTokens": {
|
||||
"message": "토큰 추가"
|
||||
},
|
||||
"addSuggestedTokens": {
|
||||
"message": "제안된 토큰 추가"
|
||||
},
|
||||
"addAcquiredTokens": {
|
||||
"message": "메타마스크를 통해 획득한 토큰 추가"
|
||||
},
|
||||
@ -55,6 +64,9 @@
|
||||
"attemptingConnect": {
|
||||
"message": "블록체인에 접속을 시도하는 중입니다."
|
||||
},
|
||||
"attemptToCancel": {
|
||||
"message": "취소 하시겠습니까?"
|
||||
},
|
||||
"attributions": {
|
||||
"message": "속성"
|
||||
},
|
||||
@ -110,6 +122,15 @@
|
||||
"cancel": {
|
||||
"message": "취소"
|
||||
},
|
||||
"cancelAttempt": {
|
||||
"message": "취소 시도"
|
||||
},
|
||||
"cancellationGasFee": {
|
||||
"message": "취소 가스 수수료"
|
||||
},
|
||||
"cancelN": {
|
||||
"message": "모든 $1 트랜잭션 취소"
|
||||
},
|
||||
"classicInterface": {
|
||||
"message": "예전 인터페이스"
|
||||
},
|
||||
@ -220,7 +241,10 @@
|
||||
"description": "거래 유형 (암호화폐)"
|
||||
},
|
||||
"currentConversion": {
|
||||
"message": "선택된 단위"
|
||||
"message": "현재 통화"
|
||||
},
|
||||
"currentLanguage": {
|
||||
"message": "현재 언어"
|
||||
},
|
||||
"currentNetwork": {
|
||||
"message": "현재 네트워크"
|
||||
@ -340,6 +364,9 @@
|
||||
"exchangeRate": {
|
||||
"message": "환율"
|
||||
},
|
||||
"expandView": {
|
||||
"message": "큰 화면으로 보기"
|
||||
},
|
||||
"exportPrivateKey": {
|
||||
"message": "개인키 내보내기"
|
||||
},
|
||||
@ -457,6 +484,9 @@
|
||||
"hideTokenPrompt": {
|
||||
"message": "토큰 숨기기?"
|
||||
},
|
||||
"history": {
|
||||
"message": "히스토리"
|
||||
},
|
||||
"howToDeposit": {
|
||||
"message": "어떤 방법으로 이더를 입금하시겠습니까?"
|
||||
},
|
||||
@ -486,6 +516,9 @@
|
||||
"importUsingSeed": {
|
||||
"message": "계정 시드 구문으로 가져오기"
|
||||
},
|
||||
"info": {
|
||||
"message": "정보"
|
||||
},
|
||||
"infoHelp": {
|
||||
"message": "정보 및 도움말"
|
||||
},
|
||||
@ -539,7 +572,7 @@
|
||||
"message": "최대"
|
||||
},
|
||||
"learnMore": {
|
||||
"message": "더 배우기."
|
||||
"message": "더 알아보기."
|
||||
},
|
||||
"ledgerAccountRestriction": {
|
||||
"message": "새 계정을 추가하려면 최소 마지막 계정을 사용해야 합니다."
|
||||
@ -590,6 +623,9 @@
|
||||
"metamaskDescription": {
|
||||
"message": "메타마스크는 이더리움을 위한 안전한 신분 저장소입니다."
|
||||
},
|
||||
"metamaskVersion": {
|
||||
"message": "메타마스크 버전"
|
||||
},
|
||||
"metamaskSeedWords": {
|
||||
"message": "메타마스크 시드 단어"
|
||||
},
|
||||
@ -610,7 +646,7 @@
|
||||
"description": "사용자는 계정을 가져오기 위해서 파일을 추가후 계속 진행해야 합니다"
|
||||
},
|
||||
"needImportPassword": {
|
||||
"message": "선택 된 파일에 대한 비밀번호를 입력해주세요.",
|
||||
"message": "선택된 파일에 대한 비밀번호를 입력해주세요.",
|
||||
"description": "계정을 가져오기 위해서 비밀번호와 파일이 필요합니다."
|
||||
},
|
||||
"negativeETH": {
|
||||
@ -727,6 +763,9 @@
|
||||
"pasteSeed": {
|
||||
"message": "시드 구문을 이곳에 붙여넣어 주세요!"
|
||||
},
|
||||
"pending": {
|
||||
"message": "펜딩 중"
|
||||
},
|
||||
"personalAddressDetected": {
|
||||
"message": "개인 주소가 탐지됨. 토큰 컨트랙트 주소를 입력하세요."
|
||||
},
|
||||
@ -755,6 +794,9 @@
|
||||
"qrCode": {
|
||||
"message": "QR 코드 보기"
|
||||
},
|
||||
"queue": {
|
||||
"message": "큐"
|
||||
},
|
||||
"readdToken": {
|
||||
"message": "옵션 메뉴에서 “토큰 추가”를 눌러서 추후에 다시 이 토큰을 추가하실 수 있습니다."
|
||||
},
|
||||
@ -773,6 +815,18 @@
|
||||
"refundAddress": {
|
||||
"message": "환불받을 주소"
|
||||
},
|
||||
"reject": {
|
||||
"message": "거부"
|
||||
},
|
||||
"rejectAll": {
|
||||
"message": "모두 거부"
|
||||
},
|
||||
"rejectTxsN": {
|
||||
"message": "$1 트랜잭션 거부"
|
||||
},
|
||||
"rejectTxsDescription": {
|
||||
"message": "$1 트랜잭션을 거부합니다."
|
||||
},
|
||||
"rejected": {
|
||||
"message": "거부됨"
|
||||
},
|
||||
@ -892,6 +946,9 @@
|
||||
"selectCurrency": {
|
||||
"message": "통화 선택"
|
||||
},
|
||||
"selectLocale": {
|
||||
"message": "언어 선택"
|
||||
},
|
||||
"selectService": {
|
||||
"message": "서비스 선택"
|
||||
},
|
||||
@ -907,6 +964,12 @@
|
||||
"sendTokens": {
|
||||
"message": "토큰 전송"
|
||||
},
|
||||
"sentEther": {
|
||||
"message": "전송된 이더"
|
||||
},
|
||||
"sentTokens": {
|
||||
"message": "전송된 토큰"
|
||||
},
|
||||
"separateEachWord": {
|
||||
"message": "각 단어는 공백 한칸으로 분리합니다"
|
||||
},
|
||||
@ -934,9 +997,6 @@
|
||||
"settings": {
|
||||
"message": "설정"
|
||||
},
|
||||
"info": {
|
||||
"message": "정보"
|
||||
},
|
||||
"shapeshiftBuy": {
|
||||
"message": "Shapeshift를 통해서 구매하기"
|
||||
},
|
||||
@ -946,12 +1006,21 @@
|
||||
"showQRCode": {
|
||||
"message": "QR 코드 보기"
|
||||
},
|
||||
"showHexData": {
|
||||
"message": "Hex 데이터 보기"
|
||||
},
|
||||
"showHexDataDescription": {
|
||||
"message": "선택하면 전송화면의 hex 데이터 필드 값을 보여줍니다."
|
||||
},
|
||||
"sign": {
|
||||
"message": "서명"
|
||||
},
|
||||
"signed": {
|
||||
"message": "서명됨"
|
||||
},
|
||||
"signatureRequest": {
|
||||
"message": "서명 요청"
|
||||
},
|
||||
"signMessage": {
|
||||
"message": "메시지 서명"
|
||||
},
|
||||
@ -1049,6 +1118,9 @@
|
||||
"total": {
|
||||
"message": "합계"
|
||||
},
|
||||
"transaction": {
|
||||
"message": "트랜잭션"
|
||||
},
|
||||
"transactions": {
|
||||
"message": "트랜잭션"
|
||||
},
|
||||
@ -1095,6 +1167,9 @@
|
||||
"unavailable": {
|
||||
"message": "이용할 수 없음"
|
||||
},
|
||||
"units": {
|
||||
"message": "단위"
|
||||
},
|
||||
"unknown": {
|
||||
"message": "알 수 없음"
|
||||
},
|
||||
@ -1165,11 +1240,14 @@
|
||||
"whatsThis": {
|
||||
"message": "이것은 무엇인가요?"
|
||||
},
|
||||
"yesLetsTry": {
|
||||
"message": "네, 시도해보겠습니다."
|
||||
},
|
||||
"yourSigRequested": {
|
||||
"message": "서명을 요청 중입니다."
|
||||
},
|
||||
"youSign": {
|
||||
"message": "서명 중입니다"
|
||||
"message": "서명합니다"
|
||||
},
|
||||
"yourPrivateSeedPhrase": {
|
||||
"message": "개인 시드 구문"
|
||||
|
1213
app/_locales/pl/messages.json
Normal file
1213
app/_locales/pl/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
14
app/images/eth.svg
Normal file
14
app/images/eth.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#38393A;}
|
||||
</style>
|
||||
<title>deposit-eth</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="deposit-eth" transform="translate(0.000000, 14.000000)">
|
||||
<path id="Shape" class="st0" d="M19.9,16L7.5,8.7L19.9,26L32.3,8.7L19.9,16L19.9,16z M20.1-14L7.7,6.4l12.4,7.3l12.4-7.2L20.1-14z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 670 B |
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "__MSG_appName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "4.14.0",
|
||||
"version": "4.16.0",
|
||||
"manifest_version": 2,
|
||||
"author": "https://metamask.io",
|
||||
"description": "__MSG_appDescription__",
|
||||
|
@ -2,6 +2,9 @@
|
||||
* @file The entry point for the web extension singleton process.
|
||||
*/
|
||||
|
||||
// this needs to run before anything else
|
||||
require('./lib/setupFetchDebugging')()
|
||||
|
||||
const urlUtil = require('url')
|
||||
const endOfStream = require('end-of-stream')
|
||||
const pump = require('pump')
|
||||
|
@ -135,17 +135,22 @@ function doctypeCheck () {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the current document extension
|
||||
* Returns whether or not the extension (suffix) of the current document is prohibited
|
||||
*
|
||||
* @returns {boolean} {@code true} if the current extension is not prohibited
|
||||
* This checks {@code window.location.pathname} against a set of file extensions
|
||||
* that should not have web3 injected into them. This check is indifferent of query parameters
|
||||
* in the location.
|
||||
*
|
||||
* @returns {boolean} whether or not the extension of the current document is prohibited
|
||||
*/
|
||||
function suffixCheck () {
|
||||
var prohibitedTypes = ['xml', 'pdf']
|
||||
var currentUrl = window.location.href
|
||||
var currentRegex
|
||||
const prohibitedTypes = [
|
||||
/\.xml$/,
|
||||
/\.pdf$/,
|
||||
]
|
||||
const currentUrl = window.location.pathname
|
||||
for (let i = 0; i < prohibitedTypes.length; i++) {
|
||||
currentRegex = new RegExp(`\\.${prohibitedTypes[i]}$`)
|
||||
if (currentRegex.test(currentUrl)) {
|
||||
if (prohibitedTypes[i].test(currentUrl)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,9 @@ class PreferencesController {
|
||||
lostIdentities: {},
|
||||
seedWords: null,
|
||||
forgottenPassword: false,
|
||||
preferences: {
|
||||
useETHAsPrimaryCurrency: true,
|
||||
},
|
||||
}, opts.initState)
|
||||
|
||||
this.diagnostics = opts.diagnostics
|
||||
@ -463,6 +466,33 @@ class PreferencesController {
|
||||
getFeatureFlags () {
|
||||
return this.store.getState().featureFlags
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the `preferences` property, which is an object. These are user-controlled features
|
||||
* found in the settings page.
|
||||
* @param {string} preference The preference to enable or disable.
|
||||
* @param {boolean} value Indicates whether or not the preference should be enabled or disabled.
|
||||
* @returns {Promise<object>} Promises a new object; the updated preferences object.
|
||||
*/
|
||||
setPreference (preference, value) {
|
||||
const currentPreferences = this.getPreferences()
|
||||
const updatedPreferences = {
|
||||
...currentPreferences,
|
||||
[preference]: value,
|
||||
}
|
||||
|
||||
this.store.updateState({ preferences: updatedPreferences })
|
||||
return Promise.resolve(updatedPreferences)
|
||||
}
|
||||
|
||||
/**
|
||||
* A getter for the `preferences` property
|
||||
* @returns {object} A key-boolean map of user-selected preferences.
|
||||
*/
|
||||
getPreferences () {
|
||||
return this.store.getState().preferences
|
||||
}
|
||||
|
||||
//
|
||||
// PRIVATE METHODS
|
||||
//
|
||||
|
@ -366,7 +366,40 @@ class TransactionController extends EventEmitter {
|
||||
this.txStateManager.setTxStatusSubmitted(txId)
|
||||
}
|
||||
|
||||
confirmTransaction (txId) {
|
||||
/**
|
||||
* Sets the status of the transaction to confirmed and sets the status of nonce duplicates as
|
||||
* dropped if the txParams have data it will fetch the txReceipt
|
||||
* @param {number} txId - The tx's ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async confirmTransaction (txId) {
|
||||
// get the txReceipt before marking the transaction confirmed
|
||||
// to ensure the receipt is gotten before the ui revives the tx
|
||||
const txMeta = this.txStateManager.getTx(txId)
|
||||
|
||||
if (!txMeta) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const txReceipt = await this.query.getTransactionReceipt(txMeta.hash)
|
||||
|
||||
// It seems that sometimes the numerical values being returned from
|
||||
// this.query.getTransactionReceipt are BN instances and not strings.
|
||||
const gasUsed = typeof txReceipt.gasUsed !== 'string'
|
||||
? txReceipt.gasUsed.toString(16)
|
||||
: txReceipt.gasUsed
|
||||
|
||||
txMeta.txReceipt = {
|
||||
...txReceipt,
|
||||
gasUsed,
|
||||
}
|
||||
|
||||
this.txStateManager.updateTx(txMeta, 'transactions#confirmTransaction - add txReceipt')
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
}
|
||||
|
||||
this.txStateManager.setTxStatusConfirmed(txId)
|
||||
this._markNonceDuplicatesDropped(txId)
|
||||
}
|
||||
|
@ -400,6 +400,11 @@ class TransactionStateManager extends EventEmitter {
|
||||
*/
|
||||
_setTxStatus (txId, status) {
|
||||
const txMeta = this.getTx(txId)
|
||||
|
||||
if (!txMeta) {
|
||||
return
|
||||
}
|
||||
|
||||
txMeta.status = status
|
||||
setTimeout(() => {
|
||||
try {
|
||||
|
@ -27,6 +27,8 @@ var metamaskStream = new LocalMessageDuplexStream({
|
||||
|
||||
// compose the inpage provider
|
||||
var inpageProvider = new MetamaskInpageProvider(metamaskStream)
|
||||
// set a high max listener count to avoid unnecesary warnings
|
||||
inpageProvider.setMaxListeners(100)
|
||||
|
||||
// Augment the provider with its enable method
|
||||
inpageProvider.enable = function (options = {}) {
|
||||
|
34
app/scripts/lib/setupFetchDebugging.js
Normal file
34
app/scripts/lib/setupFetchDebugging.js
Normal file
@ -0,0 +1,34 @@
|
||||
module.exports = setupFetchDebugging
|
||||
|
||||
//
|
||||
// This is a utility to help resolve cases where `window.fetch` throws a
|
||||
// `TypeError: Failed to Fetch` without any stack or context for the request
|
||||
// https://github.com/getsentry/sentry-javascript/pull/1293
|
||||
//
|
||||
|
||||
function setupFetchDebugging() {
|
||||
if (!global.fetch) return
|
||||
const originalFetch = global.fetch
|
||||
|
||||
global.fetch = wrappedFetch
|
||||
|
||||
async function wrappedFetch(...args) {
|
||||
const initialStack = getCurrentStack()
|
||||
try {
|
||||
return await originalFetch.call(window, ...args)
|
||||
} catch (err) {
|
||||
console.warn('FetchDebugger - fetch encountered an Error', err)
|
||||
console.warn('FetchDebugger - overriding stack to point of original call')
|
||||
err.stack = initialStack
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentStack() {
|
||||
try {
|
||||
throw new Error('Fake error for generating stack trace')
|
||||
} catch (err) {
|
||||
return err.stack
|
||||
}
|
||||
}
|
@ -129,6 +129,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
provider: this.provider,
|
||||
blockTracker: this.blockTracker,
|
||||
})
|
||||
|
||||
// start and stop polling for balances based on activeControllerConnections
|
||||
this.on('controllerConnectionChanged', (activeControllerConnections) => {
|
||||
if (activeControllerConnections > 0) {
|
||||
@ -138,6 +139,11 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
}
|
||||
})
|
||||
|
||||
// ensure accountTracker updates balances after network change
|
||||
this.networkController.on('networkDidChange', () => {
|
||||
this.accountTracker._updateAccounts()
|
||||
})
|
||||
|
||||
// key mgmt
|
||||
const additionalKeyrings = [TrezorKeyring, LedgerBridgeKeyring]
|
||||
this.keyringController = new KeyringController({
|
||||
@ -387,6 +393,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab, preferencesController),
|
||||
setAccountLabel: nodeify(preferencesController.setAccountLabel, preferencesController),
|
||||
setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController),
|
||||
setPreference: nodeify(preferencesController.setPreference, preferencesController),
|
||||
|
||||
// BlacklistController
|
||||
whitelistPhishingDomain: this.whitelistPhishingDomain.bind(this),
|
||||
|
@ -107,7 +107,10 @@
|
||||
"maxModeOn": false,
|
||||
"editingTransactionId": null
|
||||
},
|
||||
"currentLocale": "en"
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useETHAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -150,7 +150,10 @@
|
||||
"maxModeOn": false,
|
||||
"editingTransactionId": null
|
||||
},
|
||||
"currentLocale": "en"
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useETHAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -108,7 +108,10 @@
|
||||
"maxModeOn": false,
|
||||
"editingTransactionId": null
|
||||
},
|
||||
"currentLocale": "en"
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useETHAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -37,7 +37,10 @@
|
||||
"shapeShiftTxList": [],
|
||||
"lostAccounts": [],
|
||||
"tokens": [],
|
||||
"currentLocale": "en"
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useETHAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -109,7 +109,10 @@
|
||||
"maxModeOn": false,
|
||||
"editingTransactionId": null
|
||||
},
|
||||
"currentLocale": "en"
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useETHAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -102,7 +102,10 @@
|
||||
"shapeShiftTxList": [{"depositAddress":"34vJ3AfmNcLiziA4VFgEVcQTwxVLD1qkke","depositType":"BTC","key":"shapeshift","response":{"status":"no_deposits","address":"34vJ3AfmNcLiziA4VFgEVcQTwxVLD1qkke"},"time":1522377459106}],
|
||||
"lostAccounts": [],
|
||||
"send": {},
|
||||
"currentLocale": "en"
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useETHAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
226
package-lock.json
generated
226
package-lock.json
generated
@ -9691,12 +9691,13 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.10.0",
|
||||
"ethereumjs-util": "^5.0.0"
|
||||
@ -9771,12 +9772,13 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.10.0",
|
||||
"ethereumjs-util": "^5.0.0"
|
||||
@ -9897,9 +9899,9 @@
|
||||
}
|
||||
},
|
||||
"eth-json-rpc-middleware": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-3.1.1.tgz",
|
||||
"integrity": "sha512-5mRpjmszVQbKaUk3kiKkP9+hyyD3ZIg3mJ+jiydZ46cfNbrFVzfTDvZKnYLPrQPEi4+CaYqF+7XnIHIGztd9JQ==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-3.1.3.tgz",
|
||||
"integrity": "sha512-glp/mCefhsqrgVOTTuYlHYiTL+9mMPfaZsuQv4vnRg3kqNigblS1nqARaMeVW9WOM8ssh9TqIFpuUr7JDgNmKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async": "^2.5.0",
|
||||
@ -10002,12 +10004,13 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.10.0",
|
||||
"ethereumjs-util": "^5.0.0"
|
||||
@ -10068,12 +10071,13 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.10.0",
|
||||
"ethereumjs-util": "^5.0.0"
|
||||
@ -10333,12 +10337,13 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.10.0",
|
||||
"ethereumjs-util": "^5.0.0"
|
||||
@ -10371,21 +10376,21 @@
|
||||
}
|
||||
},
|
||||
"eth-token-tracker": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/eth-token-tracker/-/eth-token-tracker-1.1.4.tgz",
|
||||
"integrity": "sha1-Kf8kV9Zr+juO5JDoP/QP0M8s7EE=",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/eth-token-tracker/-/eth-token-tracker-1.1.5.tgz",
|
||||
"integrity": "sha512-dyNzEt62i5vpbAAHHj6kEVxSHg/WqCr7TBq1Sbs4y0PvsxcvfWLJpEYtJilndg36H7nJHGadgmHqGW5mYbcNfw==",
|
||||
"requires": {
|
||||
"deep-equal": "^1.0.1",
|
||||
"eth-block-tracker": "^1.0.7",
|
||||
"ethjs": "^0.2.7",
|
||||
"ethjs-contract": "^0.1.9",
|
||||
"ethjs-query": "^0.2.6",
|
||||
"ethjs": "^0.3.6",
|
||||
"ethjs-contract": "^0.2.1",
|
||||
"ethjs-query": "^0.3.7",
|
||||
"human-standard-token-abi": "^1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"babelify": {
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz",
|
||||
"integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=",
|
||||
"requires": {
|
||||
"babel-core": "^6.0.14",
|
||||
@ -10408,129 +10413,102 @@
|
||||
"ethjs-util": "^0.1.3",
|
||||
"pify": "^2.3.0",
|
||||
"tape": "^4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/ethjs/-/ethjs-0.2.9.tgz",
|
||||
"integrity": "sha1-yagNR7ydVg9Z53gEnSIlXlgfMSs=",
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/ethjs/-/ethjs-0.3.9.tgz",
|
||||
"integrity": "sha512-gOQzA3tDUjoLpNONSOALJ/rUFtHi5tXl2mholHasF1cvXhoddqi06yU4OJFJu9AGd6n9v9ywzHlYeIKg1t1hdw==",
|
||||
"requires": {
|
||||
"bn.js": "4.11.6",
|
||||
"ethjs-abi": "0.2.0",
|
||||
"ethjs-contract": "0.1.9",
|
||||
"ethjs-filter": "0.1.5",
|
||||
"ethjs-abi": "0.2.1",
|
||||
"ethjs-contract": "0.2.2",
|
||||
"ethjs-filter": "0.1.8",
|
||||
"ethjs-provider-http": "0.1.6",
|
||||
"ethjs-query": "0.3.0",
|
||||
"ethjs-query": "0.3.7",
|
||||
"ethjs-unit": "0.1.6",
|
||||
"ethjs-util": "0.1.3",
|
||||
"js-sha3": "0.5.5",
|
||||
"number-to-bn": "1.7.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethjs-format": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-format/-/ethjs-format-0.2.3.tgz",
|
||||
"integrity": "sha1-m9hnyu6CstvtmEYAuzAiDPPLWDA=",
|
||||
"requires": {
|
||||
"bn.js": "4.11.6",
|
||||
"ethjs-schema": "^0.1.6",
|
||||
"ethjs-util": "0.1.3",
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"number-to-bn": "1.7.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
},
|
||||
"ethjs-query": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-query/-/ethjs-query-0.3.0.tgz",
|
||||
"integrity": "sha1-CAmNYQ+BvV+VSnpXq0mJ9+mBX8Q=",
|
||||
"requires": {
|
||||
"ethjs-format": "0.2.3",
|
||||
"ethjs-rpc": "0.1.5"
|
||||
}
|
||||
},
|
||||
"ethjs-schema": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-schema/-/ethjs-schema-0.1.9.tgz",
|
||||
"integrity": "sha1-hYwqXacGrgSBK0zosetLSSHjMJI="
|
||||
},
|
||||
"ethjs-util": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.3.tgz",
|
||||
"integrity": "sha1-39XqSkANxeQhqInK9H4IGtp4u1U=",
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-contract": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-contract/-/ethjs-contract-0.1.9.tgz",
|
||||
"integrity": "sha1-HCdmiWpW1H7B1tZhgpxJzDilUgo=",
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-contract/-/ethjs-contract-0.2.2.tgz",
|
||||
"integrity": "sha512-xxPqEjsULQ/QNWuvX6Ako0PGs5RxALA8N/H3+boLvnaXDFZVGpD7H63H1gBCRTZyYqCldPpVlVHuw/rD45vazw==",
|
||||
"requires": {
|
||||
"ethjs-abi": "0.2.0",
|
||||
"ethjs-filter": "0.1.5",
|
||||
"ethjs-filter": "0.1.8",
|
||||
"ethjs-util": "0.1.3",
|
||||
"js-sha3": "0.5.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethjs-util": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.3.tgz",
|
||||
"integrity": "sha1-39XqSkANxeQhqInK9H4IGtp4u1U=",
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-format": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-format/-/ethjs-format-0.2.2.tgz",
|
||||
"integrity": "sha1-1zs6YFwuElcHn3B3/VRI6ZjOD80=",
|
||||
"ethjs-abi": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-abi/-/ethjs-abi-0.2.0.tgz",
|
||||
"integrity": "sha1-0+LCIQEVIPxJm3FoIDbBT8wvWyU=",
|
||||
"requires": {
|
||||
"bn.js": "4.11.6",
|
||||
"ethjs-schema": "0.1.5",
|
||||
"ethjs-util": "0.1.3",
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"number-to-bn": "1.7.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethjs-util": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.3.tgz",
|
||||
"integrity": "sha1-39XqSkANxeQhqInK9H4IGtp4u1U=",
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
"js-sha3": "0.5.5",
|
||||
"number-to-bn": "1.7.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-query": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-query/-/ethjs-query-0.2.9.tgz",
|
||||
"integrity": "sha1-om5rTzhpnpLzSyGE51x4lDKcQvE=",
|
||||
"version": "0.3.7",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-query/-/ethjs-query-0.3.7.tgz",
|
||||
"integrity": "sha512-TZnKUwfkWjy0SowFdPLtmsytCorHi0i4vvkQn7Jg8rZt33cRzKhuzOwKr/G3vdigCc+ePXOhUGMcJSAPlOG44A==",
|
||||
"requires": {
|
||||
"ethjs-format": "0.2.2",
|
||||
"ethjs-rpc": "0.1.5"
|
||||
"ethjs-format": "0.2.7",
|
||||
"ethjs-rpc": "0.2.0",
|
||||
"promise-to-callback": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"ethjs-schema": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-schema/-/ethjs-schema-0.1.5.tgz",
|
||||
"integrity": "sha1-WXQOOzl3vNu5sRvDBoIB6Kzquw0="
|
||||
"ethjs-util": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.3.tgz",
|
||||
"integrity": "sha1-39XqSkANxeQhqInK9H4IGtp4u1U=",
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-abi": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-abi/-/ethjs-abi-0.2.1.tgz",
|
||||
"integrity": "sha1-4KepOn6BFjqUR3utVu3lJKtt5TM=",
|
||||
"requires": {
|
||||
"bn.js": "4.11.6",
|
||||
"js-sha3": "0.5.5",
|
||||
"number-to-bn": "1.7.0"
|
||||
}
|
||||
},
|
||||
"ethjs-filter": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-filter/-/ethjs-filter-0.1.8.tgz",
|
||||
"integrity": "sha512-qTDPskDL2UadHwjvM8A+WG9HwM4/FoSY3p3rMJORkHltYcAuiQZd2otzOYKcL5w2Q3sbAkW/E3yt/FPFL/AVXA=="
|
||||
},
|
||||
"ethjs-query": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-query/-/ethjs-query-0.3.8.tgz",
|
||||
"integrity": "sha512-/J5JydqrOzU8O7VBOwZKUWXxHDGr46VqNjBCJgBVNNda+tv7Xc8Y2uJc6aMHHVbeN3YOQ7YRElgIc0q1CI02lQ==",
|
||||
"requires": {
|
||||
"babel-runtime": "^6.26.0",
|
||||
"ethjs-format": "0.2.7",
|
||||
"ethjs-rpc": "0.2.0",
|
||||
"promise-to-callback": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"ethjs-rpc": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-rpc/-/ethjs-rpc-0.2.0.tgz",
|
||||
"integrity": "sha512-RINulkNZTKnj4R/cjYYtYMnFFaBcVALzbtEJEONrrka8IeoarNB9Jbzn+2rT00Cv8y/CxAI+GgY1d0/i2iQeOg==",
|
||||
"requires": {
|
||||
"promise-to-callback": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"human-standard-token-abi": {
|
||||
"version": "1.0.2",
|
||||
@ -10541,6 +10519,11 @@
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz",
|
||||
"integrity": "sha1-uvDA6MVK1ZA0R9+Wreekobynmko="
|
||||
},
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -10561,12 +10544,13 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.10.0",
|
||||
"ethereumjs-util": "^5.0.0"
|
||||
@ -10799,12 +10783,13 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.10.0",
|
||||
"ethereumjs-util": "^5.0.0"
|
||||
@ -13752,12 +13737,14 @@
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.10.0",
|
||||
"ethereumjs-util": "^5.0.0"
|
||||
@ -32800,6 +32787,7 @@
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
|
||||
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
@ -33825,6 +33813,7 @@
|
||||
"resolved": "https://registry.npmjs.org/web3/-/web3-0.20.3.tgz",
|
||||
"integrity": "sha1-yqRDc9yIFayHZ73ba6cwc5ZMqos=",
|
||||
"requires": {
|
||||
"bignumber.js": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934",
|
||||
"crypto-js": "^3.1.4",
|
||||
"utf8": "^2.1.1",
|
||||
"xhr2": "*",
|
||||
@ -33833,7 +33822,7 @@
|
||||
"dependencies": {
|
||||
"bignumber.js": {
|
||||
"version": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934",
|
||||
"from": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934"
|
||||
"from": "git+https://github.com/frozeman/bignumber.js-nolookahead.git"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -34231,7 +34220,8 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"underscore": "1.8.3",
|
||||
"web3-core-helpers": "1.0.0-beta.34"
|
||||
"web3-core-helpers": "1.0.0-beta.34",
|
||||
"websocket": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2"
|
||||
},
|
||||
"dependencies": {
|
||||
"underscore": {
|
||||
@ -34241,8 +34231,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"websocket": {
|
||||
"version": "git://github.com/frozeman/WebSocket-Node.git#7004c39c42ac98875ab61126e5b4a925430f592c",
|
||||
"from": "git://github.com/frozeman/WebSocket-Node.git#7004c39c42ac98875ab61126e5b4a925430f592c",
|
||||
"version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2",
|
||||
"from": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^2.2.0",
|
||||
"nan": "^2.3.3",
|
||||
@ -34856,7 +34847,8 @@
|
||||
"yaeti": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
|
||||
"integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc="
|
||||
"integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "2.1.2",
|
||||
|
@ -124,7 +124,7 @@
|
||||
"eth-phishing-detect": "^1.1.4",
|
||||
"eth-query": "^2.1.2",
|
||||
"eth-sig-util": "^2.0.2",
|
||||
"eth-token-tracker": "^1.1.4",
|
||||
"eth-token-tracker": "^1.1.5",
|
||||
"eth-trezor-keyring": "^0.1.0",
|
||||
"ethereumjs-abi": "^0.6.4",
|
||||
"ethereumjs-tx": "^1.3.0",
|
||||
@ -261,7 +261,7 @@
|
||||
"eslint-plugin-json": "^1.2.0",
|
||||
"eslint-plugin-mocha": "^5.0.0",
|
||||
"eslint-plugin-react": "^7.4.0",
|
||||
"eth-json-rpc-middleware": "^3.1.1",
|
||||
"eth-json-rpc-middleware": "^3.1.3",
|
||||
"eth-keyring-controller": "^3.3.1",
|
||||
"fetch-mock": "^6.5.2",
|
||||
"file-loader": "^1.1.11",
|
||||
|
@ -286,7 +286,7 @@ describe('Using MetaMask with an existing account', function () {
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const inputAddress = await findElement(driver, By.css('input[placeholder="Recipient Address"]'))
|
||||
const inputAmount = await findElement(driver, By.css('.currency-display__input'))
|
||||
const inputAmount = await findElement(driver, By.css('.unit-input__input'))
|
||||
await inputAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970')
|
||||
await inputAmount.sendKeys('1')
|
||||
|
||||
|
@ -383,7 +383,7 @@ describe('MetaMask', function () {
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const inputAddress = await findElement(driver, By.css('input[placeholder="Recipient Address"]'))
|
||||
const inputAmount = await findElement(driver, By.css('.currency-display__input'))
|
||||
const inputAmount = await findElement(driver, By.css('.unit-input__input'))
|
||||
await inputAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970')
|
||||
await inputAmount.sendKeys('1')
|
||||
|
||||
@ -662,7 +662,7 @@ describe('MetaMask', function () {
|
||||
})
|
||||
|
||||
it('clicks on the Add Token button', async () => {
|
||||
const addToken = await driver.findElement(By.css('.wallet-view__add-token-button'))
|
||||
const addToken = await driver.findElement(By.xpath(`//div[contains(text(), 'Add Token')]`))
|
||||
await addToken.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
@ -702,7 +702,7 @@ describe('MetaMask', function () {
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const inputAddress = await findElement(driver, By.css('input[placeholder="Recipient Address"]'))
|
||||
const inputAmount = await findElement(driver, By.css('.currency-display__input'))
|
||||
const inputAmount = await findElement(driver, By.css('.unit-input__input'))
|
||||
await inputAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970')
|
||||
await inputAmount.sendKeys('50')
|
||||
|
||||
@ -834,8 +834,8 @@ describe('MetaMask', function () {
|
||||
await save.click()
|
||||
await driver.wait(until.stalenessOf(gasModal))
|
||||
|
||||
const gasFeeInputs = await findElements(driver, By.css('.confirm-detail-row__eth'))
|
||||
assert.equal(await gasFeeInputs[0].getText(), '♦ 0.0006')
|
||||
const gasFeeInputs = await findElements(driver, By.css('.confirm-detail-row__primary'))
|
||||
assert.equal(await gasFeeInputs[0].getText(), '0.0006')
|
||||
})
|
||||
|
||||
it('submits the transaction', async function () {
|
||||
@ -957,8 +957,8 @@ describe('MetaMask', function () {
|
||||
await save.click()
|
||||
await driver.wait(until.stalenessOf(gasModal))
|
||||
|
||||
const gasFeeInputs = await findElements(driver, By.css('.confirm-detail-row__eth'))
|
||||
assert.equal(await gasFeeInputs[0].getText(), '♦ 0.0006')
|
||||
const gasFeeInputs = await findElements(driver, By.css('.confirm-detail-row__primary'))
|
||||
assert.equal(await gasFeeInputs[0].getText(), '0.0006')
|
||||
})
|
||||
|
||||
it('submits the transaction', async function () {
|
||||
@ -1002,7 +1002,7 @@ describe('MetaMask', function () {
|
||||
|
||||
describe('Add existing token using search', () => {
|
||||
it('clicks on the Add Token button', async () => {
|
||||
const addToken = await findElement(driver, By.xpath(`//button[contains(text(), 'Add Token')]`))
|
||||
const addToken = await findElement(driver, By.xpath(`//div[contains(text(), 'Add Token')]`))
|
||||
await addToken.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
@ -1,140 +0,0 @@
|
||||
const reactTriggerChange = require('react-trigger-change')
|
||||
const {
|
||||
timeout,
|
||||
queryAsync,
|
||||
findAsync,
|
||||
} = require('../../lib/util')
|
||||
|
||||
QUnit.module('Add token flow')
|
||||
|
||||
QUnit.test('successful add token flow', (assert) => {
|
||||
const done = assert.async()
|
||||
runAddTokenFlowTest(assert)
|
||||
.then(done)
|
||||
.catch(err => {
|
||||
assert.notOk(err, `Error was thrown: ${err.stack}`)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
async function runAddTokenFlowTest (assert, done) {
|
||||
const selectState = await queryAsync($, 'select')
|
||||
selectState.val('add token')
|
||||
reactTriggerChange(selectState[0])
|
||||
|
||||
// Used to set values on TextField input component
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
||||
window.HTMLInputElement.prototype, 'value'
|
||||
).set
|
||||
|
||||
// Check that no tokens have been added
|
||||
assert.ok($('.token-list-item').length === 0, 'no tokens added')
|
||||
|
||||
// Go to Add Token screen
|
||||
let addTokenButton = await queryAsync($, 'button.btn-primary.wallet-view__add-token-button')
|
||||
assert.ok(addTokenButton[0], 'add token button present')
|
||||
addTokenButton[0].click()
|
||||
|
||||
// Verify Add Token screen
|
||||
let addTokenWrapper = await queryAsync($, '.page-container')
|
||||
assert.ok(addTokenWrapper[0], 'add token wrapper renders')
|
||||
|
||||
let addTokenTitle = await queryAsync($, '.page-container__title')
|
||||
assert.equal(addTokenTitle[0].textContent, 'Add Tokens', 'add token title is correct')
|
||||
|
||||
// Cancel Add Token
|
||||
const cancelAddTokenButton = await queryAsync($, 'button.btn-default.btn--large.page-container__footer-button')
|
||||
assert.ok(cancelAddTokenButton[0], 'cancel add token button present')
|
||||
cancelAddTokenButton.click()
|
||||
|
||||
assert.ok($('.wallet-view')[0], 'cancelled and returned to account detail wallet view')
|
||||
|
||||
// Return to Add Token Screen
|
||||
addTokenButton = await queryAsync($, 'button.btn-primary.wallet-view__add-token-button')
|
||||
assert.ok(addTokenButton[0], 'add token button present')
|
||||
addTokenButton[0].click()
|
||||
|
||||
// Verify Add Token Screen
|
||||
addTokenWrapper = await queryAsync($, '.page-container')
|
||||
addTokenTitle = await queryAsync($, '.page-container__title')
|
||||
assert.ok(addTokenWrapper[0], 'add token wrapper renders')
|
||||
assert.equal(addTokenTitle[0].textContent, 'Add Tokens', 'add token title is correct')
|
||||
|
||||
// Search for token
|
||||
const searchInput = (await findAsync(addTokenWrapper, '#search-tokens'))[0]
|
||||
searchInput.focus()
|
||||
await timeout(1000)
|
||||
nativeInputValueSetter.call(searchInput, 'a')
|
||||
searchInput.dispatchEvent(new Event('input', { bubbles: true}))
|
||||
|
||||
// Click token to add
|
||||
const tokenWrapper = await queryAsync($, 'div.token-list__token')
|
||||
assert.ok(tokenWrapper[0], 'token found')
|
||||
const tokenImageProp = tokenWrapper.find('.token-list__token-icon').css('background-image')
|
||||
const tokenImageUrl = tokenImageProp.slice(5, -2)
|
||||
tokenWrapper[0].click()
|
||||
|
||||
// Click Next button
|
||||
const nextButton = await queryAsync($, 'button.btn-primary.btn--large')
|
||||
assert.equal(nextButton[0].textContent, 'Next', 'next button rendered')
|
||||
nextButton[0].click()
|
||||
|
||||
// Confirm Add token
|
||||
const confirmAddToken = await queryAsync($, '.confirm-add-token')
|
||||
assert.ok(confirmAddToken[0], 'confirm add token rendered')
|
||||
assert.ok($('button.btn-primary.btn--large')[0], 'confirm add token button found')
|
||||
$('button.btn-primary.btn--large')[0].click()
|
||||
|
||||
// Verify added token image
|
||||
let heroBalance = await queryAsync($, '.transaction-view-balance__balance-container')
|
||||
assert.ok(heroBalance, 'rendered hero balance')
|
||||
assert.ok(tokenImageUrl.indexOf(heroBalance.find('img').attr('src')) > -1, 'token added')
|
||||
|
||||
// Return to Add Token Screen
|
||||
addTokenButton = await queryAsync($, 'button.btn-primary.wallet-view__add-token-button')
|
||||
assert.ok(addTokenButton[0], 'add token button present')
|
||||
addTokenButton[0].click()
|
||||
|
||||
addTokenWrapper = await queryAsync($, '.page-container')
|
||||
const addTokenTabs = await queryAsync($, '.page-container__tab')
|
||||
assert.equal(addTokenTabs.length, 2, 'expected number of tabs')
|
||||
assert.equal(addTokenTabs[1].textContent, 'Custom Token', 'Custom Token tab present')
|
||||
assert.ok(addTokenTabs[1], 'add custom token tab present')
|
||||
addTokenTabs[1].click()
|
||||
await timeout(1000)
|
||||
|
||||
// Input token contract address
|
||||
const customInput = (await findAsync(addTokenWrapper, '#custom-address'))[0]
|
||||
customInput.focus()
|
||||
await timeout(1000)
|
||||
nativeInputValueSetter.call(customInput, '0x177af043D3A1Aed7cc5f2397C70248Fc6cDC056c')
|
||||
customInput.dispatchEvent(new Event('input', { bubbles: true}))
|
||||
|
||||
|
||||
// Click Next button
|
||||
// nextButton = await queryAsync($, 'button.btn-primary--lg')
|
||||
// assert.equal(nextButton[0].textContent, 'Next', 'next button rendered')
|
||||
// nextButton[0].click()
|
||||
|
||||
// // Verify symbol length error since contract address won't return symbol
|
||||
const errorMessage = await queryAsync($, '#custom-symbol-helper-text')
|
||||
assert.ok(errorMessage[0], 'error rendered')
|
||||
|
||||
$('button.btn-default.btn--large')[0].click()
|
||||
|
||||
// await timeout(100000)
|
||||
|
||||
// Confirm Add token
|
||||
// assert.equal(
|
||||
// $('.page-container__subtitle')[0].textContent,
|
||||
// 'Would you like to add these tokens?',
|
||||
// 'confirm add token rendered'
|
||||
// )
|
||||
// assert.ok($('button.btn-primary--lg')[0], 'confirm add token button found')
|
||||
// $('button.btn-primary--lg')[0].click()
|
||||
|
||||
// Verify added token image
|
||||
heroBalance = await queryAsync($, '.transaction-view-balance__balance-container')
|
||||
assert.ok(heroBalance, 'rendered hero balance')
|
||||
assert.ok(heroBalance.find('.identicon')[0], 'token added')
|
||||
}
|
@ -40,7 +40,7 @@ async function customizeGas (assert, price, limit, ethFee, usdFee) {
|
||||
const sendGasField = await queryAsync($, '.send-v2__gas-fee-display')
|
||||
|
||||
assert.equal(
|
||||
(await findAsync(sendGasField, '.currency-display__input-wrapper > input')).val(),
|
||||
(await findAsync(sendGasField, '.currency-display-component'))[0].textContent,
|
||||
ethFee,
|
||||
'send gas field should show customized gas total'
|
||||
)
|
||||
@ -97,9 +97,9 @@ async function runSendFlowTest (assert, done) {
|
||||
assert.equal(sendToAccountAddress, '0x2f8D4a878cFA04A6E60D46362f5644DeAb66572D', 'send to dropdown selects the correct address')
|
||||
|
||||
const sendAmountField = await queryAsync($, '.send-v2__form-row:eq(2)')
|
||||
sendAmountField.find('.currency-display')[0].click()
|
||||
sendAmountField.find('.unit-input')[0].click()
|
||||
|
||||
const sendAmountFieldInput = await findAsync(sendAmountField, '.currency-display__input')
|
||||
const sendAmountFieldInput = await findAsync(sendAmountField, '.unit-input__input')
|
||||
sendAmountFieldInput.val('5.1')
|
||||
reactTriggerChange(sendAmountField.find('input')[0])
|
||||
|
||||
@ -112,9 +112,9 @@ async function runSendFlowTest (assert, done) {
|
||||
errorMessage = $('.send-v2__error')
|
||||
assert.equal(errorMessage.length, 0, 'send should stop rendering amount error message after amount is corrected')
|
||||
|
||||
await customizeGas(assert, 0, 21000, '0', '$0.00 USD')
|
||||
await customizeGas(assert, 1, 21000, '0.000021', '$0.03 USD')
|
||||
await customizeGas(assert, 500, 60000, '0.03', '$36.03 USD')
|
||||
await customizeGas(assert, 0, 21000, '0 ETH', '$0.00 USD')
|
||||
await customizeGas(assert, 1, 21000, '0.000021 ETH', '$0.03 USD')
|
||||
await customizeGas(assert, 500, 60000, '0.03 ETH', '$36.03 USD')
|
||||
|
||||
const sendButton = await queryAsync($, 'button.btn-primary.btn--large.page-container__footer-button')
|
||||
assert.equal(sendButton[0].textContent, 'Next', 'next button rendered')
|
||||
@ -130,11 +130,11 @@ async function runSendFlowTest (assert, done) {
|
||||
const confirmToName = (await queryAsync($, '.sender-to-recipient__name')).last()
|
||||
assert.equal(confirmToName[0].textContent, 'Send Account 3', 'confirm screen should show correct to name')
|
||||
|
||||
const confirmScreenRowFiats = await queryAsync($, '.confirm-detail-row__fiat')
|
||||
const confirmScreenRowFiats = await queryAsync($, '.confirm-detail-row__secondary')
|
||||
const confirmScreenGas = confirmScreenRowFiats[0]
|
||||
assert.equal(confirmScreenGas.textContent, '$3.60', 'confirm screen should show correct gas')
|
||||
const confirmScreenTotal = confirmScreenRowFiats[1]
|
||||
assert.equal(confirmScreenTotal.textContent, '$2,405.36', 'confirm screen should show correct total')
|
||||
assert.equal(confirmScreenTotal.textContent, '$2,405.37', 'confirm screen should show correct total')
|
||||
|
||||
const confirmScreenBackButton = await queryAsync($, '.confirm-page-container-header__back-button')
|
||||
confirmScreenBackButton[0].click()
|
||||
@ -150,9 +150,9 @@ async function runSendFlowTest (assert, done) {
|
||||
sendToFieldInputInEdit.val('0xd85a4b6a394794842887b8284293d69163007bbb')
|
||||
|
||||
const sendAmountFieldInEdit = await queryAsync($, '.send-v2__form-row:eq(2)')
|
||||
sendAmountFieldInEdit.find('.currency-display')[0].click()
|
||||
sendAmountFieldInEdit.find('.unit-input')[0].click()
|
||||
|
||||
const sendAmountFieldInputInEdit = sendAmountFieldInEdit.find('.currency-display__input')
|
||||
const sendAmountFieldInputInEdit = sendAmountFieldInEdit.find('.unit-input__input')
|
||||
sendAmountFieldInputInEdit.val('1.0')
|
||||
reactTriggerChange(sendAmountFieldInputInEdit[0])
|
||||
|
||||
|
@ -1,44 +0,0 @@
|
||||
const assert = require('assert')
|
||||
const h = require('react-hyperscript')
|
||||
const { createMockStore } = require('redux-test-utils')
|
||||
const { shallowWithStore } = require('../../lib/render-helpers')
|
||||
const BalanceComponent = require('../../../ui/app/components/balance-component')
|
||||
const mockState = {
|
||||
metamask: {
|
||||
accounts: { abc: {} },
|
||||
network: 1,
|
||||
selectedAddress: 'abc',
|
||||
},
|
||||
}
|
||||
|
||||
describe('BalanceComponent', function () {
|
||||
let balanceComponent
|
||||
let store
|
||||
let component
|
||||
beforeEach(function () {
|
||||
store = createMockStore(mockState)
|
||||
component = shallowWithStore(h(BalanceComponent), store)
|
||||
balanceComponent = component.dive()
|
||||
})
|
||||
|
||||
it('shows token balance and convert to fiat value based on conversion rate', function () {
|
||||
const formattedBalance = '1.23 ETH'
|
||||
|
||||
const tokenBalance = balanceComponent.instance().getTokenBalance(formattedBalance, false)
|
||||
const fiatDisplayNumber = balanceComponent.instance().getFiatDisplayNumber(formattedBalance, 2)
|
||||
|
||||
assert.equal('1.23 ETH', tokenBalance)
|
||||
assert.equal(2.46, fiatDisplayNumber)
|
||||
})
|
||||
|
||||
it('shows only the token balance when conversion rate is not available', function () {
|
||||
const formattedBalance = '1.23 ETH'
|
||||
|
||||
const tokenBalance = balanceComponent.instance().getTokenBalance(formattedBalance, false)
|
||||
const fiatDisplayNumber = balanceComponent.instance().getFiatDisplayNumber(formattedBalance, 0)
|
||||
|
||||
assert.equal('1.23 ETH', tokenBalance)
|
||||
assert.equal('N/A', fiatDisplayNumber)
|
||||
})
|
||||
|
||||
})
|
@ -305,6 +305,12 @@ var actions = {
|
||||
updateFeatureFlags,
|
||||
UPDATE_FEATURE_FLAGS: 'UPDATE_FEATURE_FLAGS',
|
||||
|
||||
// Preferences
|
||||
setPreference,
|
||||
updatePreferences,
|
||||
UPDATE_PREFERENCES: 'UPDATE_PREFERENCES',
|
||||
setUseETHAsPrimaryCurrencyPreference,
|
||||
|
||||
setMouseUserState,
|
||||
SET_MOUSE_USER_STATE: 'SET_MOUSE_USER_STATE',
|
||||
|
||||
@ -2298,6 +2304,36 @@ function updateFeatureFlags (updatedFeatureFlags) {
|
||||
}
|
||||
}
|
||||
|
||||
function setPreference (preference, value) {
|
||||
return dispatch => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
return new Promise((resolve, reject) => {
|
||||
background.setPreference(preference, value, (err, updatedPreferences) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
|
||||
if (err) {
|
||||
dispatch(actions.displayWarning(err.message))
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
dispatch(actions.updatePreferences(updatedPreferences))
|
||||
resolve(updatedPreferences)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function updatePreferences (value) {
|
||||
return {
|
||||
type: actions.UPDATE_PREFERENCES,
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
function setUseETHAsPrimaryCurrencyPreference (value) {
|
||||
return setPreference('useETHAsPrimaryCurrency', value)
|
||||
}
|
||||
|
||||
function setNetworkNonce (networkNonce) {
|
||||
return {
|
||||
type: actions.SET_NETWORK_NONCE,
|
||||
|
@ -8,11 +8,11 @@ const h = require('react-hyperscript')
|
||||
const actions = require('../../actions')
|
||||
const { Menu, Item, Divider, CloseArea } = require('../dropdowns/components/menu')
|
||||
const Identicon = require('../identicon')
|
||||
const { formatBalance } = require('../../util')
|
||||
const { ENVIRONMENT_TYPE_POPUP } = require('../../../../app/scripts/lib/enums')
|
||||
const { getEnvironmentType } = require('../../../../app/scripts/lib/util')
|
||||
const Tooltip = require('../tooltip')
|
||||
|
||||
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
|
||||
import { PRIMARY } from '../../constants/common'
|
||||
|
||||
const {
|
||||
SETTINGS_ROUTE,
|
||||
@ -163,7 +163,6 @@ AccountMenu.prototype.renderAccounts = function () {
|
||||
const isSelected = identity.address === selectedAddress
|
||||
|
||||
const balanceValue = accounts[address] ? accounts[address].balance : ''
|
||||
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...'
|
||||
const simpleAddress = identity.address.substring(2).toLowerCase()
|
||||
|
||||
const keyring = keyrings.find((kr) => {
|
||||
@ -189,7 +188,11 @@ AccountMenu.prototype.renderAccounts = function () {
|
||||
|
||||
h('div.account-menu__account-info', [
|
||||
h('div.account-menu__name', identity.name || ''),
|
||||
h('div.account-menu__balance', formattedBalance),
|
||||
h(UserPreferencedCurrencyDisplay, {
|
||||
className: 'account-menu__balance',
|
||||
value: balanceValue,
|
||||
type: PRIMARY,
|
||||
}),
|
||||
]),
|
||||
|
||||
this.renderKeyringType(keyring),
|
||||
|
@ -0,0 +1,34 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React, {PureComponent} from 'react'
|
||||
|
||||
export default class AddTokenButton extends PureComponent {
|
||||
static contextTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
onClick: () => {},
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
onClick: PropTypes.func,
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
const { onClick } = this.props
|
||||
|
||||
return (
|
||||
<div className="add-token-button">
|
||||
<h1 className="add-token-button__help-header">{t('missingYourTokens')}</h1>
|
||||
<p className="add-token-button__help-desc">{t('clickToAdd', [t('addToken')])}</p>
|
||||
<div
|
||||
className="add-token-button__button"
|
||||
onClick={onClick}
|
||||
>
|
||||
{t('addToken')}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
1
ui/app/components/add-token-button/index.js
Normal file
1
ui/app/components/add-token-button/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './add-token-button.component'
|
26
ui/app/components/add-token-button/index.scss
Normal file
26
ui/app/components/add-token-button/index.scss
Normal file
@ -0,0 +1,26 @@
|
||||
.add-token-button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: lighten($scorpion, 25%);
|
||||
width: 185px;
|
||||
margin: 36px auto;
|
||||
text-align: center;
|
||||
|
||||
&__help-header {
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
&__help-desc {
|
||||
font-size: 0.75rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
&__button {
|
||||
font-size: 0.75rem;
|
||||
margin: 1rem;
|
||||
text-transform: uppercase;
|
||||
color: $curious-blue;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
@ -4,10 +4,11 @@ const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const TokenBalance = require('./token-balance')
|
||||
const Identicon = require('./identicon')
|
||||
import CurrencyDisplay from './currency-display'
|
||||
import UserPreferencedCurrencyDisplay from './user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../constants/common'
|
||||
const { getAssetImages, conversionRateSelector, getCurrentCurrency} = require('../selectors')
|
||||
|
||||
const { formatBalance, generateBalanceObject } = require('../util')
|
||||
const { formatBalance } = require('../util')
|
||||
|
||||
module.exports = connect(mapStateToProps)(BalanceComponent)
|
||||
|
||||
@ -65,7 +66,7 @@ BalanceComponent.prototype.renderTokenBalance = function () {
|
||||
|
||||
BalanceComponent.prototype.renderBalance = function () {
|
||||
const props = this.props
|
||||
const { shorten, account } = props
|
||||
const { account } = props
|
||||
const balanceValue = account && account.balance
|
||||
const needsParse = 'needsParse' in props ? props.needsParse : true
|
||||
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, needsParse) : '...'
|
||||
@ -80,25 +81,20 @@ BalanceComponent.prototype.renderBalance = function () {
|
||||
}
|
||||
|
||||
return h('div.flex-column.balance-display', {}, [
|
||||
h('div.token-amount', {
|
||||
style: {},
|
||||
}, this.getTokenBalance(formattedBalance, shorten)),
|
||||
|
||||
showFiat && h(CurrencyDisplay, {
|
||||
h('div.token-amount', {}, h(UserPreferencedCurrencyDisplay, {
|
||||
value: balanceValue,
|
||||
type: PRIMARY,
|
||||
ethNumberOfDecimals: 3,
|
||||
})),
|
||||
|
||||
showFiat && h(UserPreferencedCurrencyDisplay, {
|
||||
value: balanceValue,
|
||||
type: SECONDARY,
|
||||
ethNumberOfDecimals: 3,
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
BalanceComponent.prototype.getTokenBalance = function (formattedBalance, shorten) {
|
||||
const balanceObj = generateBalanceObject(formattedBalance, shorten ? 1 : 3)
|
||||
|
||||
const balanceValue = shorten ? balanceObj.shortBalance : balanceObj.balance
|
||||
const label = balanceObj.label
|
||||
|
||||
return `${balanceValue} ${label}`
|
||||
}
|
||||
|
||||
BalanceComponent.prototype.getFiatDisplayNumber = function (formattedBalance, conversionRate) {
|
||||
if (formattedBalance === 'None') return formattedBalance
|
||||
if (conversionRate === 0) return 'N/A'
|
||||
|
@ -1,16 +1,19 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../../../constants/common'
|
||||
|
||||
const ConfirmDetailRow = props => {
|
||||
const {
|
||||
label,
|
||||
fiatText,
|
||||
ethText,
|
||||
primaryText,
|
||||
secondaryText,
|
||||
onHeaderClick,
|
||||
fiatTextColor,
|
||||
primaryValueTextColor,
|
||||
headerText,
|
||||
headerTextClassName,
|
||||
value,
|
||||
} = props
|
||||
|
||||
return (
|
||||
@ -25,28 +28,57 @@ const ConfirmDetailRow = props => {
|
||||
>
|
||||
{ headerText }
|
||||
</div>
|
||||
{
|
||||
primaryText
|
||||
? (
|
||||
<div
|
||||
className="confirm-detail-row__fiat"
|
||||
style={{ color: fiatTextColor }}
|
||||
className="confirm-detail-row__primary"
|
||||
style={{ color: primaryValueTextColor }}
|
||||
>
|
||||
{ fiatText }
|
||||
{ primaryText }
|
||||
</div>
|
||||
<div className="confirm-detail-row__eth">
|
||||
{ ethText }
|
||||
) : (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="confirm-detail-row__primary"
|
||||
type={PRIMARY}
|
||||
value={value}
|
||||
showEthLogo
|
||||
ethLogoHeight="18"
|
||||
style={{ color: primaryValueTextColor }}
|
||||
hideLabel
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
secondaryText
|
||||
? (
|
||||
<div className="confirm-detail-row__secondary">
|
||||
{ secondaryText }
|
||||
</div>
|
||||
) : (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="confirm-detail-row__secondary"
|
||||
type={SECONDARY}
|
||||
value={value}
|
||||
showEthLogo
|
||||
hideLabel
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ConfirmDetailRow.propTypes = {
|
||||
label: PropTypes.string,
|
||||
fiatText: PropTypes.string,
|
||||
ethText: PropTypes.string,
|
||||
fiatTextColor: PropTypes.string,
|
||||
onHeaderClick: PropTypes.func,
|
||||
headerText: PropTypes.string,
|
||||
headerTextClassName: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
onHeaderClick: PropTypes.func,
|
||||
primaryValueTextColor: PropTypes.string,
|
||||
primaryText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
secondaryText: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
}
|
||||
|
||||
export default ConfirmDetailRow
|
||||
|
@ -18,18 +18,14 @@
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__fiat {
|
||||
&__primary {
|
||||
font-size: 1.5rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__eth {
|
||||
&__secondary {
|
||||
color: $oslo-gray;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__header-text {
|
||||
|
@ -12,17 +12,19 @@ describe('Confirm Detail Row Component', function () {
|
||||
let wrapper
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<ConfirmDetailRow
|
||||
wrapper = shallow(
|
||||
<ConfirmDetailRow
|
||||
errorType={'mockErrorType'}
|
||||
label={'mockLabel'}
|
||||
showError={false}
|
||||
fiatText = {'mockFiatText'}
|
||||
ethText = {'mockEthText'}
|
||||
fiatTextColor= {'mockColor'}
|
||||
primaryText = {'mockFiatText'}
|
||||
secondaryText = {'mockEthText'}
|
||||
primaryValueTextColor= {'mockColor'}
|
||||
onHeaderClick= {propsMethodSpies.onHeaderClick}
|
||||
headerText = {'mockHeaderText'}
|
||||
headerTextClassName = {'mockHeaderClass'}
|
||||
/>)
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
describe('render', () => {
|
||||
@ -38,16 +40,16 @@ describe('Confirm Detail Row Component', function () {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__header-text').childAt(0).text(), 'mockHeaderText')
|
||||
})
|
||||
|
||||
it('should render the fiatText as a child of the confirm-detail-row__fiat', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__fiat').childAt(0).text(), 'mockFiatText')
|
||||
it('should render the primaryText as a child of the confirm-detail-row__primary', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__primary').childAt(0).text(), 'mockFiatText')
|
||||
})
|
||||
|
||||
it('should render the ethText as a child of the confirm-detail-row__eth', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__eth').childAt(0).text(), 'mockEthText')
|
||||
it('should render the ethText as a child of the confirm-detail-row__secondary', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__secondary').childAt(0).text(), 'mockEthText')
|
||||
})
|
||||
|
||||
it('should set the fiatTextColor on confirm-detail-row__fiat', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__fiat').props().style.color, 'mockColor')
|
||||
it('should set the fiatTextColor on confirm-detail-row__primary', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__primary').props().style.color, 'mockColor')
|
||||
})
|
||||
|
||||
it('should assure the confirm-detail-row__header-text classname is correct', () => {
|
||||
@ -58,7 +60,5 @@ describe('Confirm Detail Row Component', function () {
|
||||
wrapper.find('.confirm-detail-row__header-text').props().onClick()
|
||||
assert.equal(assert.equal(propsMethodSpies.onHeaderClick.callCount, 1))
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
})
|
||||
|
@ -17,9 +17,10 @@ export default class ConfirmPageContainerContent extends Component {
|
||||
nonce: PropTypes.string,
|
||||
assetImage: PropTypes.string,
|
||||
subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
subtitleComponent: PropTypes.node,
|
||||
summaryComponent: PropTypes.node,
|
||||
title: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
titleComponent: PropTypes.func,
|
||||
titleComponent: PropTypes.node,
|
||||
warning: PropTypes.string,
|
||||
}
|
||||
|
||||
@ -54,7 +55,9 @@ export default class ConfirmPageContainerContent extends Component {
|
||||
errorKey,
|
||||
errorMessage,
|
||||
title,
|
||||
titleComponent,
|
||||
subtitle,
|
||||
subtitleComponent,
|
||||
hideSubtitle,
|
||||
identiconAddress,
|
||||
nonce,
|
||||
@ -80,7 +83,9 @@ export default class ConfirmPageContainerContent extends Component {
|
||||
})}
|
||||
action={action}
|
||||
title={title}
|
||||
titleComponent={titleComponent}
|
||||
subtitle={subtitle}
|
||||
subtitleComponent={subtitleComponent}
|
||||
hideSubtitle={hideSubtitle}
|
||||
identiconAddress={identiconAddress}
|
||||
nonce={nonce}
|
||||
|
@ -4,7 +4,18 @@ import classnames from 'classnames'
|
||||
import Identicon from '../../../identicon'
|
||||
|
||||
const ConfirmPageContainerSummary = props => {
|
||||
const { action, title, subtitle, hideSubtitle, className, identiconAddress, nonce, assetImage } = props
|
||||
const {
|
||||
action,
|
||||
title,
|
||||
titleComponent,
|
||||
subtitle,
|
||||
subtitleComponent,
|
||||
hideSubtitle,
|
||||
className,
|
||||
identiconAddress,
|
||||
nonce,
|
||||
assetImage,
|
||||
} = props
|
||||
|
||||
return (
|
||||
<div className={classnames('confirm-page-container-summary', className)}>
|
||||
@ -32,12 +43,12 @@ const ConfirmPageContainerSummary = props => {
|
||||
)
|
||||
}
|
||||
<div className="confirm-page-container-summary__title-text">
|
||||
{ title }
|
||||
{ titleComponent || title }
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
hideSubtitle || <div className="confirm-page-container-summary__subtitle">
|
||||
{ subtitle }
|
||||
{ subtitleComponent || subtitle }
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@ -47,7 +58,9 @@ const ConfirmPageContainerSummary = props => {
|
||||
ConfirmPageContainerSummary.propTypes = {
|
||||
action: PropTypes.string,
|
||||
title: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
titleComponent: PropTypes.node,
|
||||
subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
subtitleComponent: PropTypes.node,
|
||||
hideSubtitle: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
identiconAddress: PropTypes.string,
|
||||
|
@ -16,8 +16,9 @@ export default class ConfirmPageContainer extends Component {
|
||||
onEdit: PropTypes.func,
|
||||
showEdit: PropTypes.bool,
|
||||
subtitle: PropTypes.string,
|
||||
subtitleComponent: PropTypes.node,
|
||||
title: PropTypes.string,
|
||||
titleComponent: PropTypes.func,
|
||||
titleComponent: PropTypes.node,
|
||||
// Sender to Recipient
|
||||
fromAddress: PropTypes.string,
|
||||
fromName: PropTypes.string,
|
||||
@ -65,6 +66,7 @@ export default class ConfirmPageContainer extends Component {
|
||||
title,
|
||||
titleComponent,
|
||||
subtitle,
|
||||
subtitleComponent,
|
||||
hideSubtitle,
|
||||
summaryComponent,
|
||||
detailsComponent,
|
||||
@ -101,6 +103,7 @@ export default class ConfirmPageContainer extends Component {
|
||||
title={title}
|
||||
titleComponent={titleComponent}
|
||||
subtitle={subtitle}
|
||||
subtitleComponent={subtitleComponent}
|
||||
hideSubtitle={hideSubtitle}
|
||||
summaryComponent={summaryComponent}
|
||||
detailsComponent={detailsComponent}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import { ETH, GWEI } from '../../constants/common'
|
||||
|
||||
export default class CurrencyDisplay extends PureComponent {
|
||||
@ -7,6 +8,8 @@ export default class CurrencyDisplay extends PureComponent {
|
||||
className: PropTypes.string,
|
||||
displayValue: PropTypes.string,
|
||||
prefix: PropTypes.string,
|
||||
prefixComponent: PropTypes.node,
|
||||
style: PropTypes.object,
|
||||
// Used in container
|
||||
currency: PropTypes.oneOf([ETH]),
|
||||
denomination: PropTypes.oneOf([GWEI]),
|
||||
@ -16,15 +19,17 @@ export default class CurrencyDisplay extends PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className, displayValue, prefix } = this.props
|
||||
const { className, displayValue, prefix, prefixComponent, style } = this.props
|
||||
const text = `${prefix || ''}${displayValue}`
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
className={classnames('currency-display-component', className)}
|
||||
style={style}
|
||||
title={text}
|
||||
>
|
||||
{ text }
|
||||
{ prefixComponent}
|
||||
<span className="currency-display-component__text">{ text }</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -2,10 +2,26 @@ import { connect } from 'react-redux'
|
||||
import CurrencyDisplay from './currency-display.component'
|
||||
import { getValueFromWeiHex, formatCurrency } from '../../helpers/confirm-transaction/util'
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { value, numberOfDecimals = 2, currency, denomination, hideLabel } = ownProps
|
||||
const mapStateToProps = state => {
|
||||
const { metamask: { currentCurrency, conversionRate } } = state
|
||||
|
||||
return {
|
||||
currentCurrency,
|
||||
conversionRate,
|
||||
}
|
||||
}
|
||||
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||
const { currentCurrency, conversionRate, ...restStateProps } = stateProps
|
||||
const {
|
||||
value,
|
||||
numberOfDecimals = 2,
|
||||
currency,
|
||||
denomination,
|
||||
hideLabel,
|
||||
...restOwnProps
|
||||
} = ownProps
|
||||
|
||||
const toCurrency = currency || currentCurrency
|
||||
const convertedValue = getValueFromWeiHex({
|
||||
value, toCurrency, conversionRate, numberOfDecimals, toDenomination: denomination,
|
||||
@ -14,8 +30,11 @@ const mapStateToProps = (state, ownProps) => {
|
||||
const displayValue = hideLabel ? formattedValue : `${formattedValue} ${toCurrency.toUpperCase()}`
|
||||
|
||||
return {
|
||||
...restStateProps,
|
||||
...dispatchProps,
|
||||
...restOwnProps,
|
||||
displayValue,
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(CurrencyDisplay)
|
||||
export default connect(mapStateToProps, null, mergeProps)(CurrencyDisplay)
|
||||
|
10
ui/app/components/currency-display/index.scss
Normal file
10
ui/app/components/currency-display/index.scss
Normal file
@ -0,0 +1,10 @@
|
||||
.currency-display-component {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__text {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
import assert from 'assert'
|
||||
import proxyquire from 'proxyquire'
|
||||
|
||||
let mapStateToProps
|
||||
let mapStateToProps, mergeProps
|
||||
|
||||
proxyquire('../currency-display.container.js', {
|
||||
'react-redux': {
|
||||
connect: ms => {
|
||||
connect: (ms, md, mp) => {
|
||||
mapStateToProps = ms
|
||||
mergeProps = mp
|
||||
return () => ({})
|
||||
},
|
||||
},
|
||||
@ -22,6 +23,20 @@ describe('CurrencyDisplay container', () => {
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('mergeProps()', () => {
|
||||
it('should return the correct props', () => {
|
||||
const mockStateProps = {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
}
|
||||
|
||||
const tests = [
|
||||
{
|
||||
props: {
|
||||
@ -98,7 +113,7 @@ describe('CurrencyDisplay container', () => {
|
||||
]
|
||||
|
||||
tests.forEach(({ props, result }) => {
|
||||
assert.deepEqual(mapStateToProps(mockState, props), result)
|
||||
assert.deepEqual(mergeProps(mockStateProps, {}, { ...props }), result)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
120
ui/app/components/currency-input/currency-input.component.js
Normal file
120
ui/app/components/currency-input/currency-input.component.js
Normal file
@ -0,0 +1,120 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import UnitInput from '../unit-input'
|
||||
import CurrencyDisplay from '../currency-display'
|
||||
import { getValueFromWeiHex, getWeiHexFromDecimalValue } from '../../helpers/conversions.util'
|
||||
import { ETH } from '../../constants/common'
|
||||
|
||||
/**
|
||||
* Component that allows user to enter currency values as a number, and props receive a converted
|
||||
* hex value in WEI. props.value, used as a default or forced value, should be a hex value, which
|
||||
* gets converted into a decimal value depending on the currency (ETH or Fiat).
|
||||
*/
|
||||
export default class CurrencyInput extends PureComponent {
|
||||
static propTypes = {
|
||||
conversionRate: PropTypes.number,
|
||||
currentCurrency: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
suffix: PropTypes.string,
|
||||
useFiat: PropTypes.bool,
|
||||
value: PropTypes.string,
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
const { value: hexValue } = props
|
||||
const decimalValue = hexValue ? this.getDecimalValue(props) : 0
|
||||
|
||||
this.state = {
|
||||
decimalValue,
|
||||
hexValue,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { value: prevPropsHexValue } = prevProps
|
||||
const { value: propsHexValue } = this.props
|
||||
const { hexValue: stateHexValue } = this.state
|
||||
|
||||
if (prevPropsHexValue !== propsHexValue && propsHexValue !== stateHexValue) {
|
||||
const decimalValue = this.getDecimalValue(this.props)
|
||||
this.setState({ hexValue: propsHexValue, decimalValue })
|
||||
}
|
||||
}
|
||||
|
||||
getDecimalValue (props) {
|
||||
const { value: hexValue, useFiat, currentCurrency, conversionRate } = props
|
||||
const decimalValueString = useFiat
|
||||
? getValueFromWeiHex({
|
||||
value: hexValue, toCurrency: currentCurrency, conversionRate, numberOfDecimals: 2,
|
||||
})
|
||||
: getValueFromWeiHex({
|
||||
value: hexValue, toCurrency: ETH, numberOfDecimals: 6,
|
||||
})
|
||||
|
||||
return Number(decimalValueString) || 0
|
||||
}
|
||||
|
||||
handleChange = decimalValue => {
|
||||
const { useFiat, currentCurrency: fromCurrency, conversionRate, onChange } = this.props
|
||||
|
||||
const hexValue = useFiat
|
||||
? getWeiHexFromDecimalValue({
|
||||
value: decimalValue, fromCurrency, conversionRate, invertConversionRate: true,
|
||||
})
|
||||
: getWeiHexFromDecimalValue({
|
||||
value: decimalValue, fromCurrency: ETH, fromDenomination: ETH, conversionRate,
|
||||
})
|
||||
|
||||
this.setState({ hexValue, decimalValue })
|
||||
onChange(hexValue)
|
||||
}
|
||||
|
||||
handleBlur = () => {
|
||||
this.props.onBlur(this.state.hexValue)
|
||||
}
|
||||
|
||||
renderConversionComponent () {
|
||||
const { useFiat, currentCurrency } = this.props
|
||||
const { hexValue } = this.state
|
||||
let currency, numberOfDecimals
|
||||
|
||||
if (useFiat) {
|
||||
// Display ETH
|
||||
currency = ETH
|
||||
numberOfDecimals = 6
|
||||
} else {
|
||||
// Display Fiat
|
||||
currency = currentCurrency
|
||||
numberOfDecimals = 2
|
||||
}
|
||||
|
||||
return (
|
||||
<CurrencyDisplay
|
||||
className="currency-input__conversion-component"
|
||||
currency={currency}
|
||||
value={hexValue}
|
||||
numberOfDecimals={numberOfDecimals}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { suffix, ...restProps } = this.props
|
||||
const { decimalValue } = this.state
|
||||
|
||||
return (
|
||||
<UnitInput
|
||||
{...restProps}
|
||||
suffix={suffix}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.handleBlur}
|
||||
value={decimalValue}
|
||||
>
|
||||
{ this.renderConversionComponent() }
|
||||
</UnitInput>
|
||||
)
|
||||
}
|
||||
}
|
27
ui/app/components/currency-input/currency-input.container.js
Normal file
27
ui/app/components/currency-input/currency-input.container.js
Normal file
@ -0,0 +1,27 @@
|
||||
import { connect } from 'react-redux'
|
||||
import CurrencyInput from './currency-input.component'
|
||||
import { ETH } from '../../constants/common'
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { metamask: { currentCurrency, conversionRate } } = state
|
||||
|
||||
return {
|
||||
currentCurrency,
|
||||
conversionRate,
|
||||
}
|
||||
}
|
||||
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||
const { currentCurrency } = stateProps
|
||||
const { useFiat } = ownProps
|
||||
const suffix = useFiat ? currentCurrency.toUpperCase() : ETH
|
||||
|
||||
return {
|
||||
...stateProps,
|
||||
...dispatchProps,
|
||||
...ownProps,
|
||||
suffix,
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null, mergeProps)(CurrencyInput)
|
1
ui/app/components/currency-input/index.js
Normal file
1
ui/app/components/currency-input/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './currency-input.container'
|
7
ui/app/components/currency-input/index.scss
Normal file
7
ui/app/components/currency-input/index.scss
Normal file
@ -0,0 +1,7 @@
|
||||
.currency-input {
|
||||
&__conversion-component {
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
padding-left: 1px;
|
||||
}
|
||||
}
|
@ -0,0 +1,239 @@
|
||||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow, mount } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
import { Provider } from 'react-redux'
|
||||
import configureMockStore from 'redux-mock-store'
|
||||
import CurrencyInput from '../currency-input.component'
|
||||
import UnitInput from '../../unit-input'
|
||||
import CurrencyDisplay from '../../currency-display'
|
||||
|
||||
describe('CurrencyInput Component', () => {
|
||||
describe('rendering', () => {
|
||||
it('should render properly without a suffix', () => {
|
||||
const wrapper = shallow(
|
||||
<CurrencyInput />
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find(UnitInput).length, 1)
|
||||
})
|
||||
|
||||
it('should render properly with a suffix', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<CurrencyInput
|
||||
suffix="ETH"
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').length, 1)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').text(), 'ETH')
|
||||
assert.equal(wrapper.find(CurrencyDisplay).length, 1)
|
||||
})
|
||||
|
||||
it('should render properly with an ETH value', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<CurrencyInput
|
||||
value="de0b6b3a7640000"
|
||||
suffix="ETH"
|
||||
currentCurrency="usd"
|
||||
conversionRate={231.06}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance()
|
||||
assert.equal(currencyInputInstance.state.decimalValue, 1)
|
||||
assert.equal(currencyInputInstance.state.hexValue, 'de0b6b3a7640000')
|
||||
assert.equal(wrapper.find('.unit-input__suffix').length, 1)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').text(), 'ETH')
|
||||
assert.equal(wrapper.find('.unit-input__input').props().value, '1')
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '$231.06 USD')
|
||||
})
|
||||
|
||||
it('should render properly with a fiat value', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<CurrencyInput
|
||||
value="f602f2234d0ea"
|
||||
suffix="USD"
|
||||
useFiat
|
||||
currentCurrency="usd"
|
||||
conversionRate={231.06}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance()
|
||||
assert.equal(currencyInputInstance.state.decimalValue, 1)
|
||||
assert.equal(currencyInputInstance.state.hexValue, 'f602f2234d0ea')
|
||||
assert.equal(wrapper.find('.unit-input__suffix').length, 1)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').text(), 'USD')
|
||||
assert.equal(wrapper.find('.unit-input__input').props().value, '1')
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '0.004328 ETH')
|
||||
})
|
||||
})
|
||||
|
||||
describe('handling actions', () => {
|
||||
const handleChangeSpy = sinon.spy()
|
||||
const handleBlurSpy = sinon.spy()
|
||||
|
||||
afterEach(() => {
|
||||
handleChangeSpy.resetHistory()
|
||||
handleBlurSpy.resetHistory()
|
||||
})
|
||||
|
||||
it('should call onChange and onBlur on input changes with the hex value for ETH', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<CurrencyInput
|
||||
onChange={handleChangeSpy}
|
||||
onBlur={handleBlurSpy}
|
||||
suffix="ETH"
|
||||
currentCurrency="usd"
|
||||
conversionRate={231.06}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(handleChangeSpy.callCount, 0)
|
||||
assert.equal(handleBlurSpy.callCount, 0)
|
||||
|
||||
const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance()
|
||||
assert.equal(currencyInputInstance.state.decimalValue, 0)
|
||||
assert.equal(currencyInputInstance.state.hexValue, undefined)
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '$0.00 USD')
|
||||
const input = wrapper.find('input')
|
||||
assert.equal(input.props().value, 0)
|
||||
|
||||
input.simulate('change', { target: { value: 1 } })
|
||||
assert.equal(handleChangeSpy.callCount, 1)
|
||||
assert.ok(handleChangeSpy.calledWith('de0b6b3a7640000'))
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '$231.06 USD')
|
||||
assert.equal(currencyInputInstance.state.decimalValue, 1)
|
||||
assert.equal(currencyInputInstance.state.hexValue, 'de0b6b3a7640000')
|
||||
|
||||
assert.equal(handleBlurSpy.callCount, 0)
|
||||
input.simulate('blur')
|
||||
assert.equal(handleBlurSpy.callCount, 1)
|
||||
assert.ok(handleBlurSpy.calledWith('de0b6b3a7640000'))
|
||||
})
|
||||
|
||||
it('should call onChange and onBlur on input changes with the hex value for fiat', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<CurrencyInput
|
||||
onChange={handleChangeSpy}
|
||||
onBlur={handleBlurSpy}
|
||||
suffix="USD"
|
||||
currentCurrency="usd"
|
||||
conversionRate={231.06}
|
||||
useFiat
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(handleChangeSpy.callCount, 0)
|
||||
assert.equal(handleBlurSpy.callCount, 0)
|
||||
|
||||
const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance()
|
||||
assert.equal(currencyInputInstance.state.decimalValue, 0)
|
||||
assert.equal(currencyInputInstance.state.hexValue, undefined)
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '0 ETH')
|
||||
const input = wrapper.find('input')
|
||||
assert.equal(input.props().value, 0)
|
||||
|
||||
input.simulate('change', { target: { value: 1 } })
|
||||
assert.equal(handleChangeSpy.callCount, 1)
|
||||
assert.ok(handleChangeSpy.calledWith('f602f2234d0ea'))
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '0.004328 ETH')
|
||||
assert.equal(currencyInputInstance.state.decimalValue, 1)
|
||||
assert.equal(currencyInputInstance.state.hexValue, 'f602f2234d0ea')
|
||||
|
||||
assert.equal(handleBlurSpy.callCount, 0)
|
||||
input.simulate('blur')
|
||||
assert.equal(handleBlurSpy.callCount, 1)
|
||||
assert.ok(handleBlurSpy.calledWith('f602f2234d0ea'))
|
||||
})
|
||||
|
||||
it('should change the state and pass in a new decimalValue when props.value changes', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
const wrapper = shallow(
|
||||
<Provider store={store}>
|
||||
<CurrencyInput
|
||||
onChange={handleChangeSpy}
|
||||
onBlur={handleBlurSpy}
|
||||
suffix="USD"
|
||||
currentCurrency="usd"
|
||||
conversionRate={231.06}
|
||||
useFiat
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
const currencyInputInstance = wrapper.find(CurrencyInput).dive()
|
||||
assert.equal(currencyInputInstance.state('decimalValue'), 0)
|
||||
assert.equal(currencyInputInstance.state('hexValue'), undefined)
|
||||
assert.equal(currencyInputInstance.find(UnitInput).props().value, 0)
|
||||
|
||||
currencyInputInstance.setProps({ value: '1ec05e43e72400' })
|
||||
currencyInputInstance.update()
|
||||
assert.equal(currencyInputInstance.state('decimalValue'), 2)
|
||||
assert.equal(currencyInputInstance.state('hexValue'), '1ec05e43e72400')
|
||||
assert.equal(currencyInputInstance.find(UnitInput).props().value, 2)
|
||||
})
|
||||
})
|
||||
})
|
@ -0,0 +1,55 @@
|
||||
import assert from 'assert'
|
||||
import proxyquire from 'proxyquire'
|
||||
|
||||
let mapStateToProps, mergeProps
|
||||
|
||||
proxyquire('../currency-input.container.js', {
|
||||
'react-redux': {
|
||||
connect: (ms, md, mp) => {
|
||||
mapStateToProps = ms
|
||||
mergeProps = mp
|
||||
return () => ({})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
describe('CurrencyInput container', () => {
|
||||
describe('mapStateToProps()', () => {
|
||||
it('should return the correct props', () => {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('mergeProps()', () => {
|
||||
it('should return the correct props', () => {
|
||||
const mockStateProps = {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
}
|
||||
const mockDispatchProps = {}
|
||||
|
||||
assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, { useFiat: true }), {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
useFiat: true,
|
||||
suffix: 'USD',
|
||||
})
|
||||
|
||||
assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, {}), {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
suffix: 'ETH',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@ -1,11 +1,17 @@
|
||||
@import './app-header/index';
|
||||
|
||||
@import './add-token-button/index';
|
||||
|
||||
@import './button-group/index';
|
||||
|
||||
@import './card/index';
|
||||
|
||||
@import './confirm-page-container/index';
|
||||
|
||||
@import './currency-input/index';
|
||||
|
||||
@import './currency-display/index';
|
||||
|
||||
@import './error-message/index';
|
||||
|
||||
@import './export-text-container/index';
|
||||
@ -49,3 +55,5 @@
|
||||
@import './app-header/index';
|
||||
|
||||
@import './sidebars/index';
|
||||
|
||||
@import './unit-input/index';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import CurrencyDisplay from '../../../currency-display'
|
||||
import { ETH } from '../../../../constants/common'
|
||||
import UserPreferencedCurrencyDisplay from '../../../user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../../../../constants/common'
|
||||
|
||||
export default class CancelTransaction extends PureComponent {
|
||||
static propTypes = {
|
||||
@ -13,15 +13,15 @@ export default class CancelTransaction extends PureComponent {
|
||||
|
||||
return (
|
||||
<div className="cancel-transaction-gas-fee">
|
||||
<CurrencyDisplay
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="cancel-transaction-gas-fee__eth"
|
||||
currency={ETH}
|
||||
value={value}
|
||||
numberOfDecimals={6}
|
||||
type={PRIMARY}
|
||||
/>
|
||||
<CurrencyDisplay
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="cancel-transaction-gas-fee__fiat"
|
||||
value={value}
|
||||
type={SECONDARY}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
@ -2,7 +2,7 @@ import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow } from 'enzyme'
|
||||
import CancelTransactionGasFee from '../cancel-transaction-gas-fee.component'
|
||||
import CurrencyDisplay from '../../../../currency-display'
|
||||
import UserPreferencedCurrencyDisplay from '../../../../user-preferenced-currency-display'
|
||||
|
||||
describe('CancelTransactionGasFee Component', () => {
|
||||
it('should render', () => {
|
||||
@ -13,12 +13,11 @@ describe('CancelTransactionGasFee Component', () => {
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find(CurrencyDisplay).length, 2)
|
||||
const ethDisplay = wrapper.find(CurrencyDisplay).at(0)
|
||||
const fiatDisplay = wrapper.find(CurrencyDisplay).at(1)
|
||||
assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 2)
|
||||
const ethDisplay = wrapper.find(UserPreferencedCurrencyDisplay).at(0)
|
||||
const fiatDisplay = wrapper.find(UserPreferencedCurrencyDisplay).at(1)
|
||||
|
||||
assert.equal(ethDisplay.props().value, '0x3b9aca00')
|
||||
assert.equal(ethDisplay.props().currency, 'ETH')
|
||||
assert.equal(ethDisplay.props().className, 'cancel-transaction-gas-fee__eth')
|
||||
|
||||
assert.equal(fiatDisplay.props().value, '0x3b9aca00')
|
||||
|
@ -1,12 +1,15 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ConfirmTransactionBase from '../confirm-transaction-base'
|
||||
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display'
|
||||
import {
|
||||
formatCurrency,
|
||||
convertTokenToFiat,
|
||||
addFiat,
|
||||
roundExponential,
|
||||
} from '../../../helpers/confirm-transaction/util'
|
||||
import { getWeiHexFromDecimalValue } from '../../../helpers/conversions.util'
|
||||
import { ETH, PRIMARY } from '../../../constants/common'
|
||||
|
||||
export default class ConfirmTokenTransactionBase extends Component {
|
||||
static contextTypes = {
|
||||
@ -36,19 +39,48 @@ export default class ConfirmTokenTransactionBase extends Component {
|
||||
})
|
||||
}
|
||||
|
||||
getSubtitle () {
|
||||
const { currentCurrency, contractExchangeRate } = this.props
|
||||
renderSubtitleComponent () {
|
||||
const { contractExchangeRate, tokenAmount } = this.props
|
||||
|
||||
if (typeof contractExchangeRate === 'undefined') {
|
||||
return this.context.t('noConversionRateAvailable')
|
||||
} else {
|
||||
const fiatTransactionAmount = this.getFiatTransactionAmount()
|
||||
const roundedFiatTransactionAmount = roundExponential(fiatTransactionAmount)
|
||||
return formatCurrency(roundedFiatTransactionAmount, currentCurrency)
|
||||
}
|
||||
const decimalEthValue = (tokenAmount * contractExchangeRate) || 0
|
||||
const hexWeiValue = getWeiHexFromDecimalValue({
|
||||
value: decimalEthValue,
|
||||
fromCurrency: ETH,
|
||||
fromDenomination: ETH,
|
||||
})
|
||||
|
||||
return typeof contractExchangeRate === 'undefined'
|
||||
? (
|
||||
<span>
|
||||
{ this.context.t('noConversionRateAvailable') }
|
||||
</span>
|
||||
) : (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
value={hexWeiValue}
|
||||
type={PRIMARY}
|
||||
showEthLogo
|
||||
hideLabel
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
getFiatTotalTextOverride () {
|
||||
renderPrimaryTotalTextOverride () {
|
||||
const { tokenAmount, tokenSymbol, ethTransactionTotal } = this.props
|
||||
const tokensText = `${tokenAmount} ${tokenSymbol}`
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span>{ `${tokensText} + ` }</span>
|
||||
<img
|
||||
src="/images/eth.svg"
|
||||
height="18"
|
||||
/>
|
||||
<span>{ ethTransactionTotal }</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
getSecondaryTotalTextOverride () {
|
||||
const { fiatTransactionTotal, currentCurrency, contractExchangeRate } = this.props
|
||||
|
||||
if (typeof contractExchangeRate === 'undefined') {
|
||||
@ -67,7 +99,6 @@ export default class ConfirmTokenTransactionBase extends Component {
|
||||
tokenAddress,
|
||||
tokenSymbol,
|
||||
tokenAmount,
|
||||
ethTransactionTotal,
|
||||
...restProps
|
||||
} = this.props
|
||||
|
||||
@ -78,9 +109,9 @@ export default class ConfirmTokenTransactionBase extends Component {
|
||||
toAddress={toAddress}
|
||||
identiconAddress={tokenAddress}
|
||||
title={tokensText}
|
||||
subtitle={this.getSubtitle()}
|
||||
ethTotalTextOverride={`${tokensText} + \u2666 ${ethTransactionTotal}`}
|
||||
fiatTotalTextOverride={this.getFiatTotalTextOverride()}
|
||||
subtitleComponent={this.renderSubtitleComponent()}
|
||||
primaryTotalTextOverride={this.renderPrimaryTotalTextOverride()}
|
||||
secondaryTotalTextOverride={this.getSecondaryTotalTextOverride()}
|
||||
{...restProps}
|
||||
/>
|
||||
)
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ConfirmPageContainer, { ConfirmDetailRow } from '../../confirm-page-container'
|
||||
import { formatCurrency } from '../../../helpers/confirm-transaction/util'
|
||||
import { isBalanceSufficient } from '../../send/send.utils'
|
||||
import { DEFAULT_ROUTE } from '../../../routes'
|
||||
import {
|
||||
@ -9,6 +8,8 @@ import {
|
||||
TRANSACTION_ERROR_KEY,
|
||||
} from '../../../constants/error-keys'
|
||||
import { CONFIRMED_STATUS, DROPPED_STATUS } from '../../../constants/transactions'
|
||||
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../../../constants/common'
|
||||
|
||||
export default class ConfirmTransactionBase extends Component {
|
||||
static contextTypes = {
|
||||
@ -36,7 +37,9 @@ export default class ConfirmTransactionBase extends Component {
|
||||
fiatTransactionTotal: PropTypes.string,
|
||||
fromAddress: PropTypes.string,
|
||||
fromName: PropTypes.string,
|
||||
hexGasTotal: PropTypes.string,
|
||||
hexTransactionAmount: PropTypes.string,
|
||||
hexTransactionFee: PropTypes.string,
|
||||
hexTransactionTotal: PropTypes.string,
|
||||
isTxReprice: PropTypes.bool,
|
||||
methodData: PropTypes.object,
|
||||
nonce: PropTypes.string,
|
||||
@ -59,8 +62,8 @@ export default class ConfirmTransactionBase extends Component {
|
||||
detailsComponent: PropTypes.node,
|
||||
errorKey: PropTypes.string,
|
||||
errorMessage: PropTypes.string,
|
||||
ethTotalTextOverride: PropTypes.string,
|
||||
fiatTotalTextOverride: PropTypes.string,
|
||||
primaryTotalTextOverride: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
secondaryTotalTextOverride: PropTypes.string,
|
||||
hideData: PropTypes.bool,
|
||||
hideDetails: PropTypes.bool,
|
||||
hideSubtitle: PropTypes.bool,
|
||||
@ -70,8 +73,10 @@ export default class ConfirmTransactionBase extends Component {
|
||||
onEditGas: PropTypes.func,
|
||||
onSubmit: PropTypes.func,
|
||||
subtitle: PropTypes.string,
|
||||
subtitleComponent: PropTypes.node,
|
||||
summaryComponent: PropTypes.node,
|
||||
title: PropTypes.string,
|
||||
titleComponent: PropTypes.node,
|
||||
valid: PropTypes.bool,
|
||||
warning: PropTypes.string,
|
||||
}
|
||||
@ -105,7 +110,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
const {
|
||||
balance,
|
||||
conversionRate,
|
||||
hexGasTotal,
|
||||
hexTransactionFee,
|
||||
txData: {
|
||||
simulationFails,
|
||||
txParams: {
|
||||
@ -116,7 +121,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
|
||||
const insufficientBalance = balance && !isBalanceSufficient({
|
||||
amount,
|
||||
gasTotal: hexGasTotal || '0x0',
|
||||
gasTotal: hexTransactionFee || '0x0',
|
||||
balance,
|
||||
conversionRate,
|
||||
})
|
||||
@ -153,13 +158,10 @@ export default class ConfirmTransactionBase extends Component {
|
||||
renderDetails () {
|
||||
const {
|
||||
detailsComponent,
|
||||
fiatTransactionFee,
|
||||
ethTransactionFee,
|
||||
currentCurrency,
|
||||
fiatTransactionTotal,
|
||||
ethTransactionTotal,
|
||||
fiatTotalTextOverride,
|
||||
ethTotalTextOverride,
|
||||
primaryTotalTextOverride,
|
||||
secondaryTotalTextOverride,
|
||||
hexTransactionFee,
|
||||
hexTransactionTotal,
|
||||
hideDetails,
|
||||
} = this.props
|
||||
|
||||
@ -167,16 +169,13 @@ export default class ConfirmTransactionBase extends Component {
|
||||
return null
|
||||
}
|
||||
|
||||
const formattedCurrency = formatCurrency(fiatTransactionTotal, currentCurrency)
|
||||
|
||||
return (
|
||||
detailsComponent || (
|
||||
<div className="confirm-page-container-content__details">
|
||||
<div className="confirm-page-container-content__gas-fee">
|
||||
<ConfirmDetailRow
|
||||
label="Gas Fee"
|
||||
fiatText={formatCurrency(fiatTransactionFee, currentCurrency)}
|
||||
ethText={`\u2666 ${ethTransactionFee}`}
|
||||
value={hexTransactionFee}
|
||||
headerText="Edit"
|
||||
headerTextClassName="confirm-detail-row__header-text--edit"
|
||||
onHeaderClick={() => this.handleEditGas()}
|
||||
@ -185,11 +184,12 @@ export default class ConfirmTransactionBase extends Component {
|
||||
<div>
|
||||
<ConfirmDetailRow
|
||||
label="Total"
|
||||
fiatText={fiatTotalTextOverride || formattedCurrency}
|
||||
ethText={ethTotalTextOverride || `\u2666 ${ethTransactionTotal}`}
|
||||
value={hexTransactionTotal}
|
||||
primaryText={primaryTotalTextOverride}
|
||||
secondaryText={secondaryTotalTextOverride}
|
||||
headerText="Amount + Gas Fee"
|
||||
headerTextClassName="confirm-detail-row__header-text--total"
|
||||
fiatTextColor="#2f9ae0"
|
||||
primaryValueTextColor="#2f9ae0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -311,6 +311,43 @@ export default class ConfirmTransactionBase extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
renderTitleComponent () {
|
||||
const { title, titleComponent, hexTransactionAmount } = this.props
|
||||
|
||||
// Title string passed in by props takes priority
|
||||
if (title) {
|
||||
return null
|
||||
}
|
||||
|
||||
return titleComponent || (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
value={hexTransactionAmount}
|
||||
type={PRIMARY}
|
||||
showEthLogo
|
||||
ethLogoHeight="26"
|
||||
hideLabel
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
renderSubtitleComponent () {
|
||||
const { subtitle, subtitleComponent, hexTransactionAmount } = this.props
|
||||
|
||||
// Subtitle string passed in by props takes priority
|
||||
if (subtitle) {
|
||||
return null
|
||||
}
|
||||
|
||||
return subtitleComponent || (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
value={hexTransactionAmount}
|
||||
type={SECONDARY}
|
||||
showEthLogo
|
||||
hideLabel
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
isTxReprice,
|
||||
@ -319,12 +356,9 @@ export default class ConfirmTransactionBase extends Component {
|
||||
toName,
|
||||
toAddress,
|
||||
methodData,
|
||||
ethTransactionAmount,
|
||||
fiatTransactionAmount,
|
||||
valid: propsValid = true,
|
||||
errorMessage,
|
||||
errorKey: propsErrorKey,
|
||||
currentCurrency,
|
||||
action,
|
||||
title,
|
||||
subtitle,
|
||||
@ -341,7 +375,6 @@ export default class ConfirmTransactionBase extends Component {
|
||||
const { submitting, submitError } = this.state
|
||||
|
||||
const { name } = methodData
|
||||
const fiatConvertedAmount = formatCurrency(fiatTransactionAmount, currentCurrency)
|
||||
const { valid, errorKey } = this.getErrorKey()
|
||||
|
||||
return (
|
||||
@ -352,8 +385,10 @@ export default class ConfirmTransactionBase extends Component {
|
||||
toAddress={toAddress}
|
||||
showEdit={onEdit && !isTxReprice}
|
||||
action={action || name || this.context.t('unknownFunction')}
|
||||
title={title || `${fiatConvertedAmount} ${currentCurrency.toUpperCase()}`}
|
||||
subtitle={subtitle || `\u2666 ${ethTransactionAmount}`}
|
||||
title={title}
|
||||
titleComponent={this.renderTitleComponent()}
|
||||
subtitle={subtitle}
|
||||
subtitleComponent={this.renderSubtitleComponent()}
|
||||
hideSubtitle={hideSubtitle}
|
||||
summaryComponent={summaryComponent}
|
||||
detailsComponent={this.renderDetails()}
|
||||
|
@ -36,7 +36,9 @@ const mapStateToProps = (state, props) => {
|
||||
fiatTransactionAmount,
|
||||
fiatTransactionFee,
|
||||
fiatTransactionTotal,
|
||||
hexGasTotal,
|
||||
hexTransactionAmount,
|
||||
hexTransactionFee,
|
||||
hexTransactionTotal,
|
||||
tokenData,
|
||||
methodData,
|
||||
txData,
|
||||
@ -87,7 +89,9 @@ const mapStateToProps = (state, props) => {
|
||||
fiatTransactionAmount,
|
||||
fiatTransactionFee,
|
||||
fiatTransactionTotal,
|
||||
hexGasTotal,
|
||||
hexTransactionAmount,
|
||||
hexTransactionFee,
|
||||
hexTransactionTotal,
|
||||
txData,
|
||||
tokenData,
|
||||
methodData,
|
||||
|
@ -48,4 +48,22 @@
|
||||
border-color: $ecstasy;
|
||||
}
|
||||
}
|
||||
|
||||
&__radio-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__radio-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__radio-label {
|
||||
padding-left: 4px;
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,8 @@ export default class SettingsTab extends PureComponent {
|
||||
sendHexData: PropTypes.bool,
|
||||
currentCurrency: PropTypes.string,
|
||||
conversionDate: PropTypes.number,
|
||||
useETHAsPrimaryCurrency: PropTypes.bool,
|
||||
setUseETHAsPrimaryCurrencyPreference: PropTypes.func,
|
||||
}
|
||||
|
||||
state = {
|
||||
@ -339,6 +341,56 @@ export default class SettingsTab extends PureComponent {
|
||||
)
|
||||
}
|
||||
|
||||
renderUseEthAsPrimaryCurrency () {
|
||||
const { t } = this.context
|
||||
const { useETHAsPrimaryCurrency, setUseETHAsPrimaryCurrencyPreference } = this.props
|
||||
|
||||
return (
|
||||
<div className="settings-page__content-row">
|
||||
<div className="settings-page__content-item">
|
||||
<span>{ t('primaryCurrencySetting') }</span>
|
||||
<div className="settings-page__content-description">
|
||||
{ t('primaryCurrencySettingDescription') }
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-page__content-item">
|
||||
<div className="settings-page__content-item-col">
|
||||
<div className="settings-tab__radio-buttons">
|
||||
<div className="settings-tab__radio-button">
|
||||
<input
|
||||
type="radio"
|
||||
id="eth-primary-currency"
|
||||
onChange={() => setUseETHAsPrimaryCurrencyPreference(true)}
|
||||
checked={Boolean(useETHAsPrimaryCurrency)}
|
||||
/>
|
||||
<label
|
||||
htmlFor="eth-primary-currency"
|
||||
className="settings-tab__radio-label"
|
||||
>
|
||||
{ t('eth') }
|
||||
</label>
|
||||
</div>
|
||||
<div className="settings-tab__radio-button">
|
||||
<input
|
||||
type="radio"
|
||||
id="fiat-primary-currency"
|
||||
onChange={() => setUseETHAsPrimaryCurrencyPreference(false)}
|
||||
checked={!useETHAsPrimaryCurrency}
|
||||
/>
|
||||
<label
|
||||
htmlFor="fiat-primary-currency"
|
||||
className="settings-tab__radio-label"
|
||||
>
|
||||
{ t('fiat') }
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { warning, isMascara } = this.props
|
||||
|
||||
@ -346,6 +398,7 @@ export default class SettingsTab extends PureComponent {
|
||||
<div className="settings-page__content">
|
||||
{ warning && <div className="settings-tab__error">{ warning }</div> }
|
||||
{ this.renderCurrentConversion() }
|
||||
{ this.renderUseEthAsPrimaryCurrency() }
|
||||
{ this.renderCurrentLocale() }
|
||||
{ this.renderNewRpcUrl() }
|
||||
{ this.renderStateLogs() }
|
||||
|
@ -11,7 +11,9 @@ import {
|
||||
updateCurrentLocale,
|
||||
setFeatureFlag,
|
||||
showModal,
|
||||
setUseETHAsPrimaryCurrencyPreference,
|
||||
} from '../../../../actions'
|
||||
import { preferencesSelector } from '../../../../selectors'
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { appState: { warning }, metamask } = state
|
||||
@ -24,6 +26,7 @@ const mapStateToProps = state => {
|
||||
isMascara,
|
||||
currentLocale,
|
||||
} = metamask
|
||||
const { useETHAsPrimaryCurrency } = preferencesSelector(state)
|
||||
|
||||
return {
|
||||
warning,
|
||||
@ -34,6 +37,7 @@ const mapStateToProps = state => {
|
||||
useBlockie,
|
||||
sendHexData,
|
||||
provider,
|
||||
useETHAsPrimaryCurrency,
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +54,9 @@ const mapDispatchToProps = dispatch => {
|
||||
},
|
||||
setHexDataFeatureFlag: shouldShow => dispatch(setFeatureFlag('sendHexData', shouldShow)),
|
||||
showResetAccountConfirmationModal: () => dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })),
|
||||
setUseETHAsPrimaryCurrencyPreference: value => {
|
||||
return dispatch(setUseETHAsPrimaryCurrencyPreference(value))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,8 @@ import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { checksumAddress } from '../../../util'
|
||||
import Identicon from '../../identicon'
|
||||
import CurrencyDisplay from '../currency-display'
|
||||
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../../../constants/common'
|
||||
|
||||
export default class AccountListItem extends Component {
|
||||
|
||||
@ -25,8 +26,6 @@ export default class AccountListItem extends Component {
|
||||
const {
|
||||
account,
|
||||
className,
|
||||
conversionRate,
|
||||
currentCurrency,
|
||||
displayAddress = false,
|
||||
displayBalance = true,
|
||||
handleClick,
|
||||
@ -57,16 +56,20 @@ export default class AccountListItem extends Component {
|
||||
{ checksumAddress(address) }
|
||||
</div>}
|
||||
|
||||
{displayBalance && <CurrencyDisplay
|
||||
className="account-list-item__account-balances"
|
||||
conversionRate={conversionRate}
|
||||
convertedBalanceClassName="account-list-item__account-secondary-balance"
|
||||
convertedCurrency={currentCurrency}
|
||||
primaryBalanceClassName="account-list-item__account-primary-balance"
|
||||
primaryCurrency="ETH"
|
||||
readOnly={true}
|
||||
{
|
||||
displayBalance && (
|
||||
<div className="account-list-item__account-balances">
|
||||
<UserPreferencedCurrencyDisplay
|
||||
type={PRIMARY}
|
||||
value={balance}
|
||||
/>}
|
||||
/>
|
||||
<UserPreferencedCurrencyDisplay
|
||||
type={SECONDARY}
|
||||
value={balance}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
</div>)
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { shallow } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
import proxyquire from 'proxyquire'
|
||||
import Identicon from '../../../identicon'
|
||||
import CurrencyDisplay from '../../currency-display'
|
||||
import UserPreferencedCurrencyDisplay from '../../../user-preferenced-currency-display'
|
||||
|
||||
const utilsMethodStubs = {
|
||||
checksumAddress: sinon.stub().returns('mockCheckSumAddress'),
|
||||
@ -114,17 +114,11 @@ describe('AccountListItem Component', function () {
|
||||
|
||||
it('should render a CurrencyDisplay with the correct props if displayBalance is true', () => {
|
||||
wrapper.setProps({ displayBalance: true })
|
||||
assert.equal(wrapper.find(CurrencyDisplay).length, 1)
|
||||
assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 2)
|
||||
assert.deepEqual(
|
||||
wrapper.find(CurrencyDisplay).props(),
|
||||
wrapper.find(UserPreferencedCurrencyDisplay).at(0).props(),
|
||||
{
|
||||
className: 'account-list-item__account-balances',
|
||||
conversionRate: 4,
|
||||
convertedBalanceClassName: 'account-list-item__account-secondary-balance',
|
||||
convertedCurrency: 'mockCurrentyCurrency',
|
||||
primaryBalanceClassName: 'account-list-item__account-primary-balance',
|
||||
primaryCurrency: 'ETH',
|
||||
readOnly: true,
|
||||
type: 'PRIMARY',
|
||||
value: 'mockBalance',
|
||||
}
|
||||
)
|
||||
@ -132,7 +126,7 @@ describe('AccountListItem Component', function () {
|
||||
|
||||
it('should not render a CurrencyDisplay if displayBalance is false', () => {
|
||||
wrapper.setProps({ displayBalance: false })
|
||||
assert.equal(wrapper.find(CurrencyDisplay).length, 0)
|
||||
assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,186 +0,0 @@
|
||||
const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const { conversionUtil, multiplyCurrencies } = require('../../../conversion-util')
|
||||
const { removeLeadingZeroes } = require('../send.utils')
|
||||
const currencyFormatter = require('currency-formatter')
|
||||
const currencies = require('currency-formatter/currencies')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const PropTypes = require('prop-types')
|
||||
|
||||
CurrencyDisplay.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
module.exports = CurrencyDisplay
|
||||
|
||||
inherits(CurrencyDisplay, Component)
|
||||
function CurrencyDisplay () {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
function toHexWei (value) {
|
||||
return conversionUtil(value, {
|
||||
fromNumericBase: 'dec',
|
||||
toNumericBase: 'hex',
|
||||
toDenomination: 'WEI',
|
||||
})
|
||||
}
|
||||
|
||||
CurrencyDisplay.prototype.componentWillMount = function () {
|
||||
this.setState({
|
||||
valueToRender: this.getValueToRender(this.props),
|
||||
})
|
||||
}
|
||||
|
||||
CurrencyDisplay.prototype.componentWillReceiveProps = function (nextProps) {
|
||||
const currentValueToRender = this.getValueToRender(this.props)
|
||||
const newValueToRender = this.getValueToRender(nextProps)
|
||||
if (currentValueToRender !== newValueToRender) {
|
||||
this.setState({
|
||||
valueToRender: newValueToRender,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
CurrencyDisplay.prototype.getAmount = function (value) {
|
||||
const { selectedToken } = this.props
|
||||
const { decimals } = selectedToken || {}
|
||||
const multiplier = Math.pow(10, Number(decimals || 0))
|
||||
|
||||
const sendAmount = multiplyCurrencies(value || '0', multiplier, {toNumericBase: 'hex'})
|
||||
|
||||
return selectedToken
|
||||
? sendAmount
|
||||
: toHexWei(value)
|
||||
}
|
||||
|
||||
CurrencyDisplay.prototype.getValueToRender = function ({ selectedToken, conversionRate, value, readOnly }) {
|
||||
if (value === '0x0') return readOnly ? '0' : ''
|
||||
const { decimals, symbol } = selectedToken || {}
|
||||
const multiplier = Math.pow(10, Number(decimals || 0))
|
||||
|
||||
return selectedToken
|
||||
? conversionUtil(ethUtil.addHexPrefix(value), {
|
||||
fromNumericBase: 'hex',
|
||||
toNumericBase: 'dec',
|
||||
toCurrency: symbol,
|
||||
conversionRate: multiplier,
|
||||
invertConversionRate: true,
|
||||
})
|
||||
: conversionUtil(ethUtil.addHexPrefix(value), {
|
||||
fromNumericBase: 'hex',
|
||||
toNumericBase: 'dec',
|
||||
fromDenomination: 'WEI',
|
||||
numberOfDecimals: 9,
|
||||
conversionRate,
|
||||
})
|
||||
}
|
||||
|
||||
CurrencyDisplay.prototype.getConvertedValueToRender = function (nonFormattedValue) {
|
||||
const { primaryCurrency, convertedCurrency, conversionRate } = this.props
|
||||
|
||||
if (conversionRate === 0 || conversionRate === null || conversionRate === undefined) {
|
||||
if (nonFormattedValue !== 0) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
let convertedValue = conversionUtil(nonFormattedValue, {
|
||||
fromNumericBase: 'dec',
|
||||
fromCurrency: primaryCurrency,
|
||||
toCurrency: convertedCurrency,
|
||||
numberOfDecimals: 2,
|
||||
conversionRate,
|
||||
})
|
||||
|
||||
convertedValue = Number(convertedValue).toFixed(2)
|
||||
const upperCaseCurrencyCode = convertedCurrency.toUpperCase()
|
||||
return currencies.find(currency => currency.code === upperCaseCurrencyCode)
|
||||
? currencyFormatter.format(Number(convertedValue), {
|
||||
code: upperCaseCurrencyCode,
|
||||
})
|
||||
: convertedValue
|
||||
}
|
||||
|
||||
CurrencyDisplay.prototype.handleChange = function (newVal) {
|
||||
this.setState({ valueToRender: removeLeadingZeroes(newVal) })
|
||||
this.props.onChange(this.getAmount(newVal))
|
||||
}
|
||||
|
||||
CurrencyDisplay.prototype.getInputWidth = function (valueToRender, readOnly) {
|
||||
const valueString = String(valueToRender)
|
||||
const valueLength = valueString.length || 1
|
||||
const decimalPointDeficit = valueString.match(/\./) ? -0.5 : 0
|
||||
return (valueLength + decimalPointDeficit + 0.75) + 'ch'
|
||||
}
|
||||
|
||||
CurrencyDisplay.prototype.onlyRenderConversions = function (convertedValueToRender) {
|
||||
const {
|
||||
convertedBalanceClassName = 'currency-display__converted-value',
|
||||
convertedCurrency,
|
||||
} = this.props
|
||||
return h('div', {
|
||||
className: convertedBalanceClassName,
|
||||
}, convertedValueToRender == null
|
||||
? this.context.t('noConversionRateAvailable')
|
||||
: `${convertedValueToRender} ${convertedCurrency.toUpperCase()}`
|
||||
)
|
||||
}
|
||||
|
||||
CurrencyDisplay.prototype.render = function () {
|
||||
const {
|
||||
className = 'currency-display',
|
||||
primaryBalanceClassName = 'currency-display__input',
|
||||
primaryCurrency,
|
||||
readOnly = false,
|
||||
inError = false,
|
||||
onBlur,
|
||||
step,
|
||||
} = this.props
|
||||
const { valueToRender } = this.state
|
||||
|
||||
const convertedValueToRender = this.getConvertedValueToRender(valueToRender)
|
||||
|
||||
return h('div', {
|
||||
className,
|
||||
style: {
|
||||
borderColor: inError ? 'red' : null,
|
||||
},
|
||||
onClick: () => {
|
||||
this.currencyInput && this.currencyInput.focus()
|
||||
},
|
||||
}, [
|
||||
|
||||
h('div.currency-display__primary-row', [
|
||||
|
||||
h('div.currency-display__input-wrapper', [
|
||||
|
||||
h('input', {
|
||||
className: primaryBalanceClassName,
|
||||
value: `${valueToRender}`,
|
||||
placeholder: '0',
|
||||
type: 'number',
|
||||
readOnly,
|
||||
...(!readOnly ? {
|
||||
onChange: e => this.handleChange(e.target.value),
|
||||
onBlur: () => onBlur(this.getAmount(valueToRender)),
|
||||
} : {}),
|
||||
ref: input => { this.currencyInput = input },
|
||||
style: {
|
||||
width: this.getInputWidth(valueToRender, readOnly),
|
||||
},
|
||||
min: 0,
|
||||
step,
|
||||
}),
|
||||
|
||||
h('span.currency-display__currency-symbol', primaryCurrency),
|
||||
|
||||
]),
|
||||
|
||||
]), this.onlyRenderConversions(convertedValueToRender),
|
||||
|
||||
])
|
||||
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
export { default } from './currency-display.js'
|
@ -1,91 +0,0 @@
|
||||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import sinon from 'sinon'
|
||||
import { shallow, mount } from 'enzyme'
|
||||
import CurrencyDisplay from '../currency-display'
|
||||
|
||||
describe('', () => {
|
||||
|
||||
const token = {
|
||||
address: '0xTest',
|
||||
symbol: 'TST',
|
||||
decimals: '13',
|
||||
}
|
||||
|
||||
it('retuns ETH value for wei value', () => {
|
||||
const wrapper = mount(<CurrencyDisplay />, {context: {t: str => str + '_t'}})
|
||||
|
||||
const value = wrapper.instance().getValueToRender({
|
||||
// 1000000000000000000
|
||||
value: 'DE0B6B3A7640000',
|
||||
})
|
||||
|
||||
assert.equal(value, 1)
|
||||
})
|
||||
|
||||
it('returns value of token based on token decimals', () => {
|
||||
const wrapper = mount(<CurrencyDisplay />, {context: {t: str => str + '_t'}})
|
||||
|
||||
const value = wrapper.instance().getValueToRender({
|
||||
selectedToken: token,
|
||||
// 1000000000000000000
|
||||
value: 'DE0B6B3A7640000',
|
||||
})
|
||||
|
||||
assert.equal(value, 100000)
|
||||
})
|
||||
|
||||
it('returns hex value with decimal adjustment', () => {
|
||||
|
||||
const wrapper = mount(
|
||||
<CurrencyDisplay
|
||||
selectedToken={token}
|
||||
/>, {context: {t: str => str + '_t'}})
|
||||
|
||||
const value = wrapper.instance().getAmount(1)
|
||||
// 10000000000000
|
||||
assert.equal(value, '9184e72a000')
|
||||
})
|
||||
|
||||
it('#getConvertedValueToRender converts input value based on conversionRate', () => {
|
||||
|
||||
const wrapper = mount(
|
||||
<CurrencyDisplay
|
||||
primaryCurrency={'usd'}
|
||||
convertedCurrency={'ja'}
|
||||
conversionRate={2}
|
||||
/>, {context: {t: str => str + '_t'}})
|
||||
|
||||
const value = wrapper.instance().getConvertedValueToRender(32)
|
||||
|
||||
assert.equal(value, 64)
|
||||
})
|
||||
|
||||
it('#onlyRenderConversions renders single element for converted currency and value', () => {
|
||||
const wrapper = mount(
|
||||
<CurrencyDisplay
|
||||
convertedCurrency={'test'}
|
||||
/>, {context: {t: str => str + '_t'}})
|
||||
|
||||
const value = wrapper.instance().onlyRenderConversions(10)
|
||||
assert.equal(value.props.className, 'currency-display__converted-value')
|
||||
assert.equal(value.props.children, '10 TEST')
|
||||
})
|
||||
|
||||
it('simulates change value in input', () => {
|
||||
const handleChangeSpy = sinon.spy()
|
||||
|
||||
const wrapper = shallow(
|
||||
<CurrencyDisplay
|
||||
onChange={handleChangeSpy}
|
||||
/>, {context: {t: str => str + '_t'}})
|
||||
|
||||
const input = wrapper.find('input')
|
||||
input.simulate('focus')
|
||||
input.simulate('change', { target: { value: '100' } })
|
||||
|
||||
assert.equal(wrapper.state().valueToRender, '100')
|
||||
assert.equal(wrapper.find('input').prop('value'), '100')
|
||||
})
|
||||
|
||||
})
|
@ -34,19 +34,25 @@ export default class AmountMaxButton extends Component {
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
const { setMaxModeTo, maxModeOn } = this.props
|
||||
onMaxClick = (event) => {
|
||||
const { setMaxModeTo } = this.props
|
||||
|
||||
return (
|
||||
<div
|
||||
className="send-v2__amount-max"
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
setMaxModeTo(true)
|
||||
this.setMaxAmount()
|
||||
}}
|
||||
}
|
||||
|
||||
render () {
|
||||
return this.props.maxModeOn
|
||||
? null
|
||||
: (
|
||||
<div>
|
||||
<span
|
||||
className="send-v2__amount-max"
|
||||
onClick={this.onMaxClick}
|
||||
>
|
||||
{!maxModeOn ? this.context.t('max') : ''}
|
||||
{this.context.t('max')}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -56,9 +56,8 @@ describe('AmountMaxButton Component', function () {
|
||||
})
|
||||
|
||||
describe('render', () => {
|
||||
it('should render a div with a send-v2__amount-max class', () => {
|
||||
assert.equal(wrapper.find('.send-v2__amount-max').length, 1)
|
||||
assert(wrapper.find('.send-v2__amount-max').is('div'))
|
||||
it('should render an element with a send-v2__amount-max class', () => {
|
||||
assert(wrapper.exists('.send-v2__amount-max'))
|
||||
})
|
||||
|
||||
it('should call setMaxModeTo and setMaxAmount when the send-v2__amount-max div is clicked', () => {
|
||||
@ -77,9 +76,9 @@ describe('AmountMaxButton Component', function () {
|
||||
)
|
||||
})
|
||||
|
||||
it('should not render text when maxModeOn is true', () => {
|
||||
it('should not render anything when maxModeOn is true', () => {
|
||||
wrapper.setProps({ maxModeOn: true })
|
||||
assert.equal(wrapper.find('.send-v2__amount-max').text(), '')
|
||||
assert.ok(!wrapper.exists('.send-v2__amount-max'))
|
||||
})
|
||||
|
||||
it('should render the expected text when maxModeOn is false', () => {
|
||||
|
@ -2,7 +2,8 @@ import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import SendRowWrapper from '../send-row-wrapper/'
|
||||
import AmountMaxButton from './amount-max-button/'
|
||||
import CurrencyDisplay from '../../currency-display'
|
||||
import UserPreferencedCurrencyInput from '../../../user-preferenced-currency-input'
|
||||
import UserPreferencedTokenInput from '../../../user-preferenced-token-input'
|
||||
|
||||
export default class SendAmountRow extends Component {
|
||||
|
||||
@ -84,16 +85,25 @@ export default class SendAmountRow extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
renderInput () {
|
||||
const { amount, inError, selectedToken } = this.props
|
||||
const Component = selectedToken ? UserPreferencedTokenInput : UserPreferencedCurrencyInput
|
||||
|
||||
return (
|
||||
<Component
|
||||
onChange={newAmount => this.validateAmount(newAmount)}
|
||||
onBlur={newAmount => {
|
||||
this.updateGas(newAmount)
|
||||
this.updateAmount(newAmount)
|
||||
}}
|
||||
error={inError}
|
||||
value={amount}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
amount,
|
||||
amountConversionRate,
|
||||
convertedCurrency,
|
||||
gasTotal,
|
||||
inError,
|
||||
primaryCurrency,
|
||||
selectedToken,
|
||||
} = this.props
|
||||
const { gasTotal, inError } = this.props
|
||||
|
||||
return (
|
||||
<SendRowWrapper
|
||||
@ -102,20 +112,7 @@ export default class SendAmountRow extends Component {
|
||||
errorType={'amount'}
|
||||
>
|
||||
{!inError && gasTotal && <AmountMaxButton />}
|
||||
<CurrencyDisplay
|
||||
conversionRate={amountConversionRate}
|
||||
convertedCurrency={convertedCurrency}
|
||||
onBlur={newAmount => {
|
||||
this.updateGas(newAmount)
|
||||
this.updateAmount(newAmount)
|
||||
}}
|
||||
onChange={newAmount => this.validateAmount(newAmount)}
|
||||
inError={inError}
|
||||
primaryCurrency={primaryCurrency || 'ETH'}
|
||||
selectedToken={selectedToken}
|
||||
value={amount}
|
||||
step="any"
|
||||
/>
|
||||
{ this.renderInput() }
|
||||
</SendRowWrapper>
|
||||
)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import SendAmountRow from '../send-amount-row.component.js'
|
||||
|
||||
import SendRowWrapper from '../../send-row-wrapper/send-row-wrapper.component'
|
||||
import AmountMaxButton from '../amount-max-button/amount-max-button.container'
|
||||
import CurrencyDisplay from '../../../currency-display'
|
||||
import UserPreferencedTokenInput from '../../../../user-preferenced-token-input'
|
||||
|
||||
const propsMethodSpies = {
|
||||
setMaxModeTo: sinon.spy(),
|
||||
@ -150,26 +150,19 @@ describe('SendAmountRow Component', function () {
|
||||
assert(wrapper.find(SendRowWrapper).childAt(0).is(AmountMaxButton))
|
||||
})
|
||||
|
||||
it('should render a CurrencyDisplay as the second child of the SendRowWrapper', () => {
|
||||
assert(wrapper.find(SendRowWrapper).childAt(1).is(CurrencyDisplay))
|
||||
it('should render a UserPreferencedTokenInput as the second child of the SendRowWrapper', () => {
|
||||
console.log('HI', wrapper.find(SendRowWrapper).childAt(1))
|
||||
assert(wrapper.find(SendRowWrapper).childAt(1).is(UserPreferencedTokenInput))
|
||||
})
|
||||
|
||||
it('should render the CurrencyDisplay with the correct props', () => {
|
||||
it('should render the UserPreferencedTokenInput with the correct props', () => {
|
||||
const {
|
||||
conversionRate,
|
||||
convertedCurrency,
|
||||
onBlur,
|
||||
onChange,
|
||||
inError,
|
||||
primaryCurrency,
|
||||
selectedToken,
|
||||
error,
|
||||
value,
|
||||
} = wrapper.find(SendRowWrapper).childAt(1).props()
|
||||
assert.equal(conversionRate, 'mockAmountConversionRate')
|
||||
assert.equal(convertedCurrency, 'mockConvertedCurrency')
|
||||
assert.equal(inError, false)
|
||||
assert.equal(primaryCurrency, 'mockPrimaryCurrency')
|
||||
assert.deepEqual(selectedToken, { address: 'mockTokenAddress' })
|
||||
assert.equal(error, false)
|
||||
assert.equal(value, 'mockAmount')
|
||||
assert.equal(SendAmountRow.prototype.updateGas.callCount, 0)
|
||||
assert.equal(SendAmountRow.prototype.updateAmount.callCount, 0)
|
||||
@ -192,11 +185,5 @@ describe('SendAmountRow Component', function () {
|
||||
['mockNewAmount']
|
||||
)
|
||||
})
|
||||
|
||||
it('should pass the default primaryCurrency to the CurrencyDisplay if primaryCurrency is falsy', () => {
|
||||
wrapper.setProps({ primaryCurrency: null })
|
||||
const { primaryCurrency } = wrapper.find(SendRowWrapper).childAt(1).props()
|
||||
assert.equal(primaryCurrency, 'ETH')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import CurrencyDisplay from '../../../../send/currency-display'
|
||||
|
||||
import UserPreferencedCurrencyDisplay from '../../../../user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../../../../../constants/common'
|
||||
|
||||
export default class GasFeeDisplay extends Component {
|
||||
|
||||
@ -19,27 +19,24 @@ export default class GasFeeDisplay extends Component {
|
||||
};
|
||||
|
||||
render () {
|
||||
const {
|
||||
conversionRate,
|
||||
gasTotal,
|
||||
onClick,
|
||||
primaryCurrency = 'ETH',
|
||||
convertedCurrency,
|
||||
gasLoadingError,
|
||||
} = this.props
|
||||
const { gasTotal, onClick, gasLoadingError } = this.props
|
||||
|
||||
return (
|
||||
<div className="send-v2__gas-fee-display">
|
||||
{gasTotal
|
||||
? <CurrencyDisplay
|
||||
primaryCurrency={primaryCurrency}
|
||||
convertedCurrency={convertedCurrency}
|
||||
? (
|
||||
<div className="currency-display">
|
||||
<UserPreferencedCurrencyDisplay
|
||||
value={gasTotal}
|
||||
conversionRate={conversionRate}
|
||||
gasLoadingError={gasLoadingError}
|
||||
convertedPrefix={'$'}
|
||||
readOnly
|
||||
type={PRIMARY}
|
||||
/>
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="currency-display__converted-value"
|
||||
value={gasTotal}
|
||||
type={SECONDARY}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
: gasLoadingError
|
||||
? <div className="currency-display.currency-display--message">
|
||||
{this.context.t('setGasPrice')}
|
||||
|
@ -2,7 +2,7 @@ import React from 'react'
|
||||
import assert from 'assert'
|
||||
import {shallow} from 'enzyme'
|
||||
import GasFeeDisplay from '../gas-fee-display.component'
|
||||
import CurrencyDisplay from '../../../../../send/currency-display'
|
||||
import UserPreferencedCurrencyDisplay from '../../../../../user-preferenced-currency-display'
|
||||
import sinon from 'sinon'
|
||||
|
||||
|
||||
@ -29,17 +29,15 @@ describe('SendGasRow Component', function () {
|
||||
|
||||
describe('render', () => {
|
||||
it('should render a CurrencyDisplay component', () => {
|
||||
assert.equal(wrapper.find(CurrencyDisplay).length, 1)
|
||||
assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 2)
|
||||
})
|
||||
|
||||
it('should render the CurrencyDisplay with the correct props', () => {
|
||||
const {
|
||||
conversionRate,
|
||||
convertedCurrency,
|
||||
type,
|
||||
value,
|
||||
} = wrapper.find(CurrencyDisplay).props()
|
||||
assert.equal(conversionRate, 20)
|
||||
assert.equal(convertedCurrency, 'mockConvertedCurrency')
|
||||
} = wrapper.find(UserPreferencedCurrencyDisplay).at(0).props()
|
||||
assert.equal(type, 'PRIMARY')
|
||||
assert.equal(value, 'mockGasTotal')
|
||||
})
|
||||
|
||||
|
1
ui/app/components/token-input/index.js
Normal file
1
ui/app/components/token-input/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './token-input.container'
|
@ -0,0 +1,308 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import assert from 'assert'
|
||||
import { shallow, mount } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
import { Provider } from 'react-redux'
|
||||
import configureMockStore from 'redux-mock-store'
|
||||
import TokenInput from '../token-input.component'
|
||||
import UnitInput from '../../unit-input'
|
||||
import CurrencyDisplay from '../../currency-display'
|
||||
|
||||
describe('TokenInput Component', () => {
|
||||
const t = key => `translate ${key}`
|
||||
|
||||
describe('rendering', () => {
|
||||
it('should render properly without a token', () => {
|
||||
const wrapper = shallow(
|
||||
<TokenInput />,
|
||||
{ context: { t } }
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find(UnitInput).length, 1)
|
||||
})
|
||||
|
||||
it('should render properly with a token', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
selectedToken={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
/>
|
||||
</Provider>,
|
||||
{ context: { t },
|
||||
childContextTypes: {
|
||||
t: PropTypes.func,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').length, 1)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').text(), 'ABC')
|
||||
assert.equal(wrapper.find('.currency-input__conversion-component').length, 1)
|
||||
assert.equal(wrapper.find('.currency-input__conversion-component').text(), 'translate noConversionRateAvailable')
|
||||
})
|
||||
|
||||
it('should render properly with a token and selectedTokenExchangeRate', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
selectedToken={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
selectedTokenExchangeRate={2}
|
||||
/>
|
||||
</Provider>,
|
||||
{ context: { t },
|
||||
childContextTypes: {
|
||||
t: PropTypes.func,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').length, 1)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').text(), 'ABC')
|
||||
assert.equal(wrapper.find(CurrencyDisplay).length, 1)
|
||||
})
|
||||
|
||||
it('should render properly with a token value for ETH', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
value="2710"
|
||||
selectedToken={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
selectedTokenExchangeRate={2}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
const tokenInputInstance = wrapper.find(TokenInput).at(0).instance()
|
||||
assert.equal(tokenInputInstance.state.decimalValue, 1)
|
||||
assert.equal(tokenInputInstance.state.hexValue, '2710')
|
||||
assert.equal(wrapper.find('.unit-input__suffix').length, 1)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').text(), 'ABC')
|
||||
assert.equal(wrapper.find('.unit-input__input').props().value, '1')
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '2 ETH')
|
||||
})
|
||||
|
||||
it('should render properly with a token value for fiat', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
value="2710"
|
||||
selectedToken={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
selectedTokenExchangeRate={2}
|
||||
showFiat
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
const tokenInputInstance = wrapper.find(TokenInput).at(0).instance()
|
||||
assert.equal(tokenInputInstance.state.decimalValue, 1)
|
||||
assert.equal(tokenInputInstance.state.hexValue, '2710')
|
||||
assert.equal(wrapper.find('.unit-input__suffix').length, 1)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').text(), 'ABC')
|
||||
assert.equal(wrapper.find('.unit-input__input').props().value, '1')
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '$462.12 USD')
|
||||
})
|
||||
})
|
||||
|
||||
describe('handling actions', () => {
|
||||
const handleChangeSpy = sinon.spy()
|
||||
const handleBlurSpy = sinon.spy()
|
||||
|
||||
afterEach(() => {
|
||||
handleChangeSpy.resetHistory()
|
||||
handleBlurSpy.resetHistory()
|
||||
})
|
||||
|
||||
it('should call onChange and onBlur on input changes with the hex value for ETH', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
onChange={handleChangeSpy}
|
||||
onBlur={handleBlurSpy}
|
||||
selectedToken={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
selectedTokenExchangeRate={2}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(handleChangeSpy.callCount, 0)
|
||||
assert.equal(handleBlurSpy.callCount, 0)
|
||||
|
||||
const tokenInputInstance = wrapper.find(TokenInput).at(0).instance()
|
||||
assert.equal(tokenInputInstance.state.decimalValue, 0)
|
||||
assert.equal(tokenInputInstance.state.hexValue, undefined)
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '0 ETH')
|
||||
const input = wrapper.find('input')
|
||||
assert.equal(input.props().value, 0)
|
||||
|
||||
input.simulate('change', { target: { value: 1 } })
|
||||
assert.equal(handleChangeSpy.callCount, 1)
|
||||
assert.ok(handleChangeSpy.calledWith('2710'))
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '2 ETH')
|
||||
assert.equal(tokenInputInstance.state.decimalValue, 1)
|
||||
assert.equal(tokenInputInstance.state.hexValue, '2710')
|
||||
|
||||
assert.equal(handleBlurSpy.callCount, 0)
|
||||
input.simulate('blur')
|
||||
assert.equal(handleBlurSpy.callCount, 1)
|
||||
assert.ok(handleBlurSpy.calledWith('2710'))
|
||||
})
|
||||
|
||||
it('should call onChange and onBlur on input changes with the hex value for fiat', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
onChange={handleChangeSpy}
|
||||
onBlur={handleBlurSpy}
|
||||
selectedToken={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
selectedTokenExchangeRate={2}
|
||||
showFiat
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(handleChangeSpy.callCount, 0)
|
||||
assert.equal(handleBlurSpy.callCount, 0)
|
||||
|
||||
const tokenInputInstance = wrapper.find(TokenInput).at(0).instance()
|
||||
assert.equal(tokenInputInstance.state.decimalValue, 0)
|
||||
assert.equal(tokenInputInstance.state.hexValue, undefined)
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '$0.00 USD')
|
||||
const input = wrapper.find('input')
|
||||
assert.equal(input.props().value, 0)
|
||||
|
||||
input.simulate('change', { target: { value: 1 } })
|
||||
assert.equal(handleChangeSpy.callCount, 1)
|
||||
assert.ok(handleChangeSpy.calledWith('2710'))
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '$462.12 USD')
|
||||
assert.equal(tokenInputInstance.state.decimalValue, 1)
|
||||
assert.equal(tokenInputInstance.state.hexValue, '2710')
|
||||
|
||||
assert.equal(handleBlurSpy.callCount, 0)
|
||||
input.simulate('blur')
|
||||
assert.equal(handleBlurSpy.callCount, 1)
|
||||
assert.ok(handleBlurSpy.calledWith('2710'))
|
||||
})
|
||||
|
||||
it('should change the state and pass in a new decimalValue when props.value changes', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
const wrapper = shallow(
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
onChange={handleChangeSpy}
|
||||
onBlur={handleBlurSpy}
|
||||
selectedToken={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
selectedTokenExchangeRate={2}
|
||||
showFiat
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
const tokenInputInstance = wrapper.find(TokenInput).dive()
|
||||
assert.equal(tokenInputInstance.state('decimalValue'), 0)
|
||||
assert.equal(tokenInputInstance.state('hexValue'), undefined)
|
||||
assert.equal(tokenInputInstance.find(UnitInput).props().value, 0)
|
||||
|
||||
tokenInputInstance.setProps({ value: '2710' })
|
||||
tokenInputInstance.update()
|
||||
assert.equal(tokenInputInstance.state('decimalValue'), 1)
|
||||
assert.equal(tokenInputInstance.state('hexValue'), '2710')
|
||||
assert.equal(tokenInputInstance.find(UnitInput).props().value, 1)
|
||||
})
|
||||
})
|
||||
})
|
@ -0,0 +1,129 @@
|
||||
import assert from 'assert'
|
||||
import proxyquire from 'proxyquire'
|
||||
|
||||
let mapStateToProps, mergeProps
|
||||
|
||||
proxyquire('../token-input.container.js', {
|
||||
'react-redux': {
|
||||
connect: (ms, md, mp) => {
|
||||
mapStateToProps = ms
|
||||
mergeProps = mp
|
||||
return () => ({})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
describe('TokenInput container', () => {
|
||||
describe('mapStateToProps()', () => {
|
||||
it('should return the correct props when send is empty', () => {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
tokens: [
|
||||
{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
],
|
||||
selectedTokenAddress: '0x1',
|
||||
contractExchangeRates: {},
|
||||
send: {},
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
currentCurrency: 'usd',
|
||||
selectedToken: {
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
selectedTokenExchangeRate: 0,
|
||||
})
|
||||
})
|
||||
|
||||
it('should return the correct props when selectedTokenAddress is not found and send is populated', () => {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
tokens: [
|
||||
{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
],
|
||||
selectedTokenAddress: '0x2',
|
||||
contractExchangeRates: {},
|
||||
send: {
|
||||
token: { address: 'test' },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
currentCurrency: 'usd',
|
||||
selectedToken: {
|
||||
address: 'test',
|
||||
},
|
||||
selectedTokenExchangeRate: 0,
|
||||
})
|
||||
})
|
||||
|
||||
it('should return the correct props when contractExchangeRates is populated', () => {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
tokens: [
|
||||
{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
],
|
||||
selectedTokenAddress: '0x1',
|
||||
contractExchangeRates: {
|
||||
'0x1': 5,
|
||||
},
|
||||
send: {},
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
currentCurrency: 'usd',
|
||||
selectedToken: {
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
selectedTokenExchangeRate: 5,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('mergeProps()', () => {
|
||||
it('should return the correct props', () => {
|
||||
const mockStateProps = {
|
||||
currentCurrency: 'usd',
|
||||
selectedToken: {
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
selectedTokenExchangeRate: 5,
|
||||
}
|
||||
|
||||
assert.deepEqual(mergeProps(mockStateProps, {}, {}), {
|
||||
currentCurrency: 'usd',
|
||||
selectedToken: {
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
selectedTokenExchangeRate: 5,
|
||||
suffix: 'ABC',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
136
ui/app/components/token-input/token-input.component.js
Normal file
136
ui/app/components/token-input/token-input.component.js
Normal file
@ -0,0 +1,136 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import UnitInput from '../unit-input'
|
||||
import CurrencyDisplay from '../currency-display'
|
||||
import { getWeiHexFromDecimalValue } from '../../helpers/conversions.util'
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
import { conversionUtil, multiplyCurrencies } from '../../conversion-util'
|
||||
import { ETH } from '../../constants/common'
|
||||
|
||||
/**
|
||||
* Component that allows user to enter token values as a number, and props receive a converted
|
||||
* hex value. props.value, used as a default or forced value, should be a hex value, which
|
||||
* gets converted into a decimal value.
|
||||
*/
|
||||
export default class TokenInput extends PureComponent {
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
currentCurrency: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
value: PropTypes.string,
|
||||
suffix: PropTypes.string,
|
||||
showFiat: PropTypes.bool,
|
||||
selectedToken: PropTypes.object,
|
||||
selectedTokenExchangeRate: PropTypes.number,
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
const { value: hexValue } = props
|
||||
const decimalValue = hexValue ? this.getDecimalValue(props) : 0
|
||||
|
||||
this.state = {
|
||||
decimalValue,
|
||||
hexValue,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { value: prevPropsHexValue } = prevProps
|
||||
const { value: propsHexValue } = this.props
|
||||
const { hexValue: stateHexValue } = this.state
|
||||
|
||||
if (prevPropsHexValue !== propsHexValue && propsHexValue !== stateHexValue) {
|
||||
const decimalValue = this.getDecimalValue(this.props)
|
||||
this.setState({ hexValue: propsHexValue, decimalValue })
|
||||
}
|
||||
}
|
||||
|
||||
getDecimalValue (props) {
|
||||
const { value: hexValue, selectedToken: { decimals, symbol } = {} } = props
|
||||
|
||||
const multiplier = Math.pow(10, Number(decimals || 0))
|
||||
const decimalValueString = conversionUtil(ethUtil.addHexPrefix(hexValue), {
|
||||
fromNumericBase: 'hex',
|
||||
toNumericBase: 'dec',
|
||||
toCurrency: symbol,
|
||||
conversionRate: multiplier,
|
||||
invertConversionRate: true,
|
||||
})
|
||||
|
||||
return Number(decimalValueString) || 0
|
||||
}
|
||||
|
||||
handleChange = decimalValue => {
|
||||
const { selectedToken: { decimals } = {}, onChange } = this.props
|
||||
|
||||
const multiplier = Math.pow(10, Number(decimals || 0))
|
||||
const hexValue = multiplyCurrencies(decimalValue || 0, multiplier, { toNumericBase: 'hex' })
|
||||
|
||||
this.setState({ hexValue, decimalValue })
|
||||
onChange(hexValue)
|
||||
}
|
||||
|
||||
handleBlur = () => {
|
||||
this.props.onBlur(this.state.hexValue)
|
||||
}
|
||||
|
||||
renderConversionComponent () {
|
||||
const { selectedTokenExchangeRate, showFiat, currentCurrency } = this.props
|
||||
const { decimalValue } = this.state
|
||||
let currency, numberOfDecimals
|
||||
|
||||
if (showFiat) {
|
||||
// Display Fiat
|
||||
currency = currentCurrency
|
||||
numberOfDecimals = 2
|
||||
} else {
|
||||
// Display ETH
|
||||
currency = ETH
|
||||
numberOfDecimals = 6
|
||||
}
|
||||
|
||||
const decimalEthValue = (decimalValue * selectedTokenExchangeRate) || 0
|
||||
const hexWeiValue = getWeiHexFromDecimalValue({
|
||||
value: decimalEthValue,
|
||||
fromCurrency: ETH,
|
||||
fromDenomination: ETH,
|
||||
})
|
||||
|
||||
return selectedTokenExchangeRate
|
||||
? (
|
||||
<CurrencyDisplay
|
||||
className="currency-input__conversion-component"
|
||||
currency={currency}
|
||||
value={hexWeiValue}
|
||||
numberOfDecimals={numberOfDecimals}
|
||||
/>
|
||||
) : (
|
||||
<div className="currency-input__conversion-component">
|
||||
{ this.context.t('noConversionRateAvailable') }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { suffix, ...restProps } = this.props
|
||||
const { decimalValue } = this.state
|
||||
|
||||
return (
|
||||
<UnitInput
|
||||
{...restProps}
|
||||
suffix={suffix}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.handleBlur}
|
||||
value={decimalValue}
|
||||
>
|
||||
{ this.renderConversionComponent() }
|
||||
</UnitInput>
|
||||
)
|
||||
}
|
||||
}
|
27
ui/app/components/token-input/token-input.container.js
Normal file
27
ui/app/components/token-input/token-input.container.js
Normal file
@ -0,0 +1,27 @@
|
||||
import { connect } from 'react-redux'
|
||||
import TokenInput from './token-input.component'
|
||||
import { getSelectedToken, getSelectedTokenExchangeRate } from '../../selectors'
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { metamask: { currentCurrency } } = state
|
||||
|
||||
return {
|
||||
currentCurrency,
|
||||
selectedToken: getSelectedToken(state),
|
||||
selectedTokenExchangeRate: getSelectedTokenExchangeRate(state),
|
||||
}
|
||||
}
|
||||
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||
const { selectedToken } = stateProps
|
||||
const suffix = selectedToken && selectedToken.symbol
|
||||
|
||||
return {
|
||||
...stateProps,
|
||||
...dispatchProps,
|
||||
...ownProps,
|
||||
suffix,
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null, mergeProps)(TokenInput)
|
@ -46,11 +46,15 @@ export function getActivities (transaction) {
|
||||
if (!Array.isArray(base) && base.status === UNAPPROVED_STATUS && base.txParams) {
|
||||
const { time, txParams: { value } = {} } = base
|
||||
return acc.concat(eventCreator(TRANSACTION_CREATED_EVENT, time, value))
|
||||
// An entry in the history may be an array of more sub-entries.
|
||||
} else if (Array.isArray(base)) {
|
||||
const events = []
|
||||
|
||||
base.forEach(entry => {
|
||||
const { op, path, value, timestamp } = entry
|
||||
const { op, path, value, timestamp: entryTimestamp } = entry
|
||||
// Not all sub-entries in a history entry have a timestamp. If the sub-entry does not have a
|
||||
// timestamp, the first sub-entry in a history entry should.
|
||||
const timestamp = entryTimestamp || base[0] && base[0].timestamp
|
||||
|
||||
if (path in eventPathsHash && op === REPLACE_OP) {
|
||||
switch (path) {
|
||||
|
@ -4,8 +4,9 @@ import classnames from 'classnames'
|
||||
import TransactionBreakdownRow from './transaction-breakdown-row'
|
||||
import Card from '../card'
|
||||
import CurrencyDisplay from '../currency-display'
|
||||
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
|
||||
import HexToDecimal from '../hex-to-decimal'
|
||||
import { ETH, GWEI } from '../../constants/common'
|
||||
import { ETH, GWEI, PRIMARY, SECONDARY } from '../../constants/common'
|
||||
import { getHexGasTotal } from '../../helpers/confirm-transaction/util'
|
||||
import { sumHexes } from '../../helpers/transactions.util'
|
||||
|
||||
@ -26,8 +27,11 @@ export default class TransactionBreakdown extends PureComponent {
|
||||
render () {
|
||||
const { t } = this.context
|
||||
const { transaction, className } = this.props
|
||||
const { txParams: { gas, gasPrice, value } = {} } = transaction
|
||||
const hexGasTotal = getHexGasTotal({ gasLimit: gas, gasPrice })
|
||||
const { txParams: { gas, gasPrice, value } = {}, txReceipt: { gasUsed } = {} } = transaction
|
||||
|
||||
const gasLimit = typeof gasUsed === 'string' ? gasUsed : gas
|
||||
|
||||
const hexGasTotal = getHexGasTotal({ gasLimit, gasPrice })
|
||||
const totalInHex = sumHexes(hexGasTotal, value)
|
||||
|
||||
return (
|
||||
@ -37,9 +41,9 @@ export default class TransactionBreakdown extends PureComponent {
|
||||
className="transaction-breakdown__card"
|
||||
>
|
||||
<TransactionBreakdownRow title={t('amount')}>
|
||||
<CurrencyDisplay
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="transaction-breakdown__value"
|
||||
currency={ETH}
|
||||
type={PRIMARY}
|
||||
value={value}
|
||||
/>
|
||||
</TransactionBreakdownRow>
|
||||
@ -52,6 +56,19 @@ export default class TransactionBreakdown extends PureComponent {
|
||||
value={gas}
|
||||
/>
|
||||
</TransactionBreakdownRow>
|
||||
{
|
||||
typeof gasUsed === 'string' && (
|
||||
<TransactionBreakdownRow
|
||||
title={`${t('gasUsed')} (${t('units')})`}
|
||||
className="transaction-breakdown__row-title"
|
||||
>
|
||||
<HexToDecimal
|
||||
className="transaction-breakdown__value"
|
||||
value={gasUsed}
|
||||
/>
|
||||
</TransactionBreakdownRow>
|
||||
)
|
||||
}
|
||||
<TransactionBreakdownRow title={t('gasPrice')}>
|
||||
<CurrencyDisplay
|
||||
className="transaction-breakdown__value"
|
||||
@ -63,14 +80,14 @@ export default class TransactionBreakdown extends PureComponent {
|
||||
</TransactionBreakdownRow>
|
||||
<TransactionBreakdownRow title={t('total')}>
|
||||
<div>
|
||||
<CurrencyDisplay
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="transaction-breakdown__value transaction-breakdown__value--eth-total"
|
||||
currency={ETH}
|
||||
type={PRIMARY}
|
||||
value={totalInHex}
|
||||
numberOfDecimals={6}
|
||||
/>
|
||||
<CurrencyDisplay
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="transaction-breakdown__value"
|
||||
type={SECONDARY}
|
||||
value={totalInHex}
|
||||
/>
|
||||
</div>
|
||||
|
@ -5,6 +5,7 @@ import { CARDS_VARIANT } from '../sender-to-recipient/sender-to-recipient.consta
|
||||
import TransactionActivityLog from '../transaction-activity-log'
|
||||
import TransactionBreakdown from '../transaction-breakdown'
|
||||
import Button from '../button'
|
||||
import Tooltip from '../tooltip'
|
||||
import prefixForNetwork from '../../../lib/etherscan-prefix-for-network'
|
||||
|
||||
export default class TransactionListItemDetails extends PureComponent {
|
||||
@ -75,6 +76,7 @@ export default class TransactionListItemDetails extends PureComponent {
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
<Tooltip title={t('viewOnEtherscan')}>
|
||||
<Button
|
||||
type="raised"
|
||||
onClick={this.handleEtherscanClick}
|
||||
@ -82,6 +84,7 @@ export default class TransactionListItemDetails extends PureComponent {
|
||||
>
|
||||
<img src="/images/arrow-popout.svg" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className="transaction-list-item-details__sender-to-recipient-container">
|
||||
|
@ -4,13 +4,14 @@ import classnames from 'classnames'
|
||||
import Identicon from '../identicon'
|
||||
import TransactionStatus from '../transaction-status'
|
||||
import TransactionAction from '../transaction-action'
|
||||
import CurrencyDisplay from '../currency-display'
|
||||
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
|
||||
import TokenCurrencyDisplay from '../token-currency-display'
|
||||
import TransactionListItemDetails from '../transaction-list-item-details'
|
||||
import { CONFIRM_TRANSACTION_ROUTE } from '../../routes'
|
||||
import { UNAPPROVED_STATUS, TOKEN_METHOD_TRANSFER } from '../../constants/transactions'
|
||||
import { ETH } from '../../constants/common'
|
||||
import { PRIMARY, SECONDARY } from '../../constants/common'
|
||||
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../app/scripts/lib/enums'
|
||||
import { getStatusKey } from '../../helpers/transactions.util'
|
||||
|
||||
export default class TransactionListItem extends PureComponent {
|
||||
static propTypes = {
|
||||
@ -102,12 +103,11 @@ export default class TransactionListItem extends PureComponent {
|
||||
prefix="-"
|
||||
/>
|
||||
) : (
|
||||
<CurrencyDisplay
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="transaction-list-item__amount transaction-list-item__amount--primary"
|
||||
value={value}
|
||||
type={PRIMARY}
|
||||
prefix="-"
|
||||
numberOfDecimals={2}
|
||||
currency={ETH}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -118,10 +118,11 @@ export default class TransactionListItem extends PureComponent {
|
||||
return token
|
||||
? null
|
||||
: (
|
||||
<CurrencyDisplay
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="transaction-list-item__amount transaction-list-item__amount--secondary"
|
||||
prefix="-"
|
||||
value={value}
|
||||
prefix="-"
|
||||
type={SECONDARY}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -167,7 +168,7 @@ export default class TransactionListItem extends PureComponent {
|
||||
</div>
|
||||
<TransactionStatus
|
||||
className="transaction-list-item__status"
|
||||
statusKey={transaction.status}
|
||||
statusKey={getStatusKey(transaction)}
|
||||
title={(
|
||||
(transaction.err && transaction.err.rpc)
|
||||
? transaction.err.rpc.message
|
||||
|
@ -25,4 +25,9 @@
|
||||
background-color: #FFF2DB;
|
||||
color: #CA810A;
|
||||
}
|
||||
|
||||
&--failed {
|
||||
background: lighten($monzo, 56%);
|
||||
color: $monzo;
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ import assert from 'assert'
|
||||
import { shallow } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
import TokenBalance from '../../token-balance'
|
||||
import CurrencyDisplay from '../../currency-display'
|
||||
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display'
|
||||
import { SEND_ROUTE } from '../../../routes'
|
||||
import TransactionViewBalance from '../transaction-view-balance.component'
|
||||
|
||||
@ -35,7 +35,7 @@ describe('TransactionViewBalance Component', () => {
|
||||
|
||||
assert.equal(wrapper.find('.transaction-view-balance').length, 1)
|
||||
assert.equal(wrapper.find('.transaction-view-balance__button').length, 2)
|
||||
assert.equal(wrapper.find(CurrencyDisplay).length, 2)
|
||||
assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 2)
|
||||
|
||||
const buttons = wrapper.find('.transaction-view-balance__buttons')
|
||||
assert.equal(propsMethodSpies.showDepositModal.callCount, 0)
|
||||
|
@ -3,9 +3,9 @@ import PropTypes from 'prop-types'
|
||||
import Button from '../button'
|
||||
import Identicon from '../identicon'
|
||||
import TokenBalance from '../token-balance'
|
||||
import CurrencyDisplay from '../currency-display'
|
||||
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
|
||||
import { SEND_ROUTE } from '../../routes'
|
||||
import { ETH } from '../../constants/common'
|
||||
import { PRIMARY, SECONDARY } from '../../constants/common'
|
||||
|
||||
export default class TransactionViewBalance extends PureComponent {
|
||||
static contextTypes = {
|
||||
@ -33,15 +33,17 @@ export default class TransactionViewBalance extends PureComponent {
|
||||
/>
|
||||
) : (
|
||||
<div className="transaction-view-balance__balance">
|
||||
<CurrencyDisplay
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="transaction-view-balance__primary-balance"
|
||||
value={balance}
|
||||
currency={ETH}
|
||||
numberOfDecimals={3}
|
||||
type={PRIMARY}
|
||||
ethNumberOfDecimals={3}
|
||||
/>
|
||||
<CurrencyDisplay
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="transaction-view-balance__secondary-balance"
|
||||
value={balance}
|
||||
type={SECONDARY}
|
||||
ethNumberOfDecimals={3}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
1
ui/app/components/unit-input/index.js
Normal file
1
ui/app/components/unit-input/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './unit-input.component'
|
44
ui/app/components/unit-input/index.scss
Normal file
44
ui/app/components/unit-input/index.scss
Normal file
@ -0,0 +1,44 @@
|
||||
.unit-input {
|
||||
min-height: 54px;
|
||||
border: 1px solid #dedede;
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
color: #4d4d4d;
|
||||
font-size: 1rem;
|
||||
padding: 8px 10px;
|
||||
position: relative;
|
||||
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
input[type="number"]::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
input[type="number"]:hover::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__input {
|
||||
color: #4d4d4d;
|
||||
font-size: 1rem;
|
||||
font-family: Roboto;
|
||||
border: none;
|
||||
outline: 0 !important;
|
||||
max-width: 22ch;
|
||||
}
|
||||
|
||||
&__input-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&--error {
|
||||
border-color: $red;
|
||||
}
|
||||
}
|
146
ui/app/components/unit-input/tests/unit-input.component.test.js
Normal file
146
ui/app/components/unit-input/tests/unit-input.component.test.js
Normal file
@ -0,0 +1,146 @@
|
||||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow, mount } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
import UnitInput from '../unit-input.component'
|
||||
|
||||
describe('UnitInput Component', () => {
|
||||
describe('rendering', () => {
|
||||
it('should render properly without a suffix', () => {
|
||||
const wrapper = shallow(
|
||||
<UnitInput />
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').length, 0)
|
||||
})
|
||||
|
||||
it('should render properly with a suffix', () => {
|
||||
const wrapper = shallow(
|
||||
<UnitInput
|
||||
suffix="ETH"
|
||||
/>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').length, 1)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').text(), 'ETH')
|
||||
})
|
||||
|
||||
it('should render properly with a child omponent', () => {
|
||||
const wrapper = shallow(
|
||||
<UnitInput>
|
||||
<div className="testing">
|
||||
TESTCOMPONENT
|
||||
</div>
|
||||
</UnitInput>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find('.testing').length, 1)
|
||||
assert.equal(wrapper.find('.testing').text(), 'TESTCOMPONENT')
|
||||
})
|
||||
|
||||
it('should render with an error class when props.error === true', () => {
|
||||
const wrapper = shallow(
|
||||
<UnitInput
|
||||
error
|
||||
/>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find('.unit-input--error').length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('handling actions', () => {
|
||||
const handleChangeSpy = sinon.spy()
|
||||
const handleBlurSpy = sinon.spy()
|
||||
|
||||
afterEach(() => {
|
||||
handleChangeSpy.resetHistory()
|
||||
handleBlurSpy.resetHistory()
|
||||
})
|
||||
|
||||
it('should focus the input on component click', () => {
|
||||
const wrapper = mount(
|
||||
<UnitInput />
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
const handleFocusSpy = sinon.spy(wrapper.instance(), 'handleFocus')
|
||||
wrapper.instance().forceUpdate()
|
||||
wrapper.update()
|
||||
assert.equal(handleFocusSpy.callCount, 0)
|
||||
wrapper.find('.unit-input').simulate('click')
|
||||
assert.equal(handleFocusSpy.callCount, 1)
|
||||
})
|
||||
|
||||
it('should call onChange on input changes with the value', () => {
|
||||
const wrapper = mount(
|
||||
<UnitInput
|
||||
onChange={handleChangeSpy}
|
||||
/>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(handleChangeSpy.callCount, 0)
|
||||
const input = wrapper.find('input')
|
||||
input.simulate('change', { target: { value: 123 } })
|
||||
assert.equal(handleChangeSpy.callCount, 1)
|
||||
assert.ok(handleChangeSpy.calledWith(123))
|
||||
assert.equal(wrapper.state('value'), 123)
|
||||
})
|
||||
|
||||
it('should call onBlur on blur with the value', () => {
|
||||
const wrapper = mount(
|
||||
<UnitInput
|
||||
onChange={handleChangeSpy}
|
||||
onBlur={handleBlurSpy}
|
||||
/>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(handleChangeSpy.callCount, 0)
|
||||
assert.equal(handleBlurSpy.callCount, 0)
|
||||
const input = wrapper.find('input')
|
||||
input.simulate('change', { target: { value: 123 } })
|
||||
assert.equal(handleChangeSpy.callCount, 1)
|
||||
assert.ok(handleChangeSpy.calledWith(123))
|
||||
assert.equal(wrapper.state('value'), 123)
|
||||
input.simulate('blur')
|
||||
assert.equal(handleBlurSpy.callCount, 1)
|
||||
assert.ok(handleBlurSpy.calledWith(123))
|
||||
})
|
||||
|
||||
it('should set the component state value with props.value', () => {
|
||||
const wrapper = mount(
|
||||
<UnitInput
|
||||
value={123}
|
||||
/>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.state('value'), 123)
|
||||
})
|
||||
|
||||
it('should update the component state value with props.value', () => {
|
||||
const wrapper = mount(
|
||||
<UnitInput
|
||||
onChange={handleChangeSpy}
|
||||
/>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(handleChangeSpy.callCount, 0)
|
||||
const input = wrapper.find('input')
|
||||
input.simulate('change', { target: { value: 123 } })
|
||||
assert.equal(wrapper.state('value'), 123)
|
||||
assert.equal(handleChangeSpy.callCount, 1)
|
||||
assert.ok(handleChangeSpy.calledWith(123))
|
||||
wrapper.setProps({ value: 456 })
|
||||
assert.equal(wrapper.state('value'), 456)
|
||||
assert.equal(handleChangeSpy.callCount, 1)
|
||||
})
|
||||
})
|
||||
})
|
104
ui/app/components/unit-input/unit-input.component.js
Normal file
104
ui/app/components/unit-input/unit-input.component.js
Normal file
@ -0,0 +1,104 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import { removeLeadingZeroes } from '../send/send.utils'
|
||||
|
||||
/**
|
||||
* Component that attaches a suffix or unit of measurement trailing user input, ex. 'ETH'. Also
|
||||
* allows rendering a child component underneath the input to, for example, display conversions of
|
||||
* the shown suffix.
|
||||
*/
|
||||
export default class UnitInput extends PureComponent {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
error: PropTypes.bool,
|
||||
onBlur: PropTypes.func,
|
||||
onChange: PropTypes.func,
|
||||
placeholder: PropTypes.string,
|
||||
suffix: PropTypes.string,
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
placeholder: '0',
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
value: props.value || '',
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { value: prevPropsValue } = prevProps
|
||||
const { value: propsValue } = this.props
|
||||
const { value: stateValue } = this.state
|
||||
|
||||
if (prevPropsValue !== propsValue && propsValue !== stateValue) {
|
||||
this.setState({ value: propsValue })
|
||||
}
|
||||
}
|
||||
|
||||
handleFocus = () => {
|
||||
this.unitInput.focus()
|
||||
}
|
||||
|
||||
handleChange = event => {
|
||||
const { value: userInput } = event.target
|
||||
let value = userInput
|
||||
|
||||
if (userInput.length && userInput.length > 1) {
|
||||
value = removeLeadingZeroes(userInput)
|
||||
}
|
||||
|
||||
this.setState({ value })
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
handleBlur = event => {
|
||||
const { onBlur } = this.props
|
||||
typeof onBlur === 'function' && onBlur(this.state.value)
|
||||
}
|
||||
|
||||
getInputWidth (value) {
|
||||
const valueString = String(value)
|
||||
const valueLength = valueString.length || 1
|
||||
const decimalPointDeficit = valueString.match(/\./) ? -0.5 : 0
|
||||
return (valueLength + decimalPointDeficit + 0.75) + 'ch'
|
||||
}
|
||||
|
||||
render () {
|
||||
const { error, placeholder, suffix, children } = this.props
|
||||
const { value } = this.state
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('unit-input', { 'unit-input--error': error })}
|
||||
onClick={this.handleFocus}
|
||||
>
|
||||
<div className="unit-input__input-container">
|
||||
<input
|
||||
type="number"
|
||||
className="unit-input__input"
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.handleBlur}
|
||||
style={{ width: this.getInputWidth(value) }}
|
||||
ref={ref => { this.unitInput = ref }}
|
||||
/>
|
||||
{
|
||||
suffix && (
|
||||
<div className="unit-input__suffix">
|
||||
{ suffix }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{ children }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export { default } from './user-preferenced-currency-display.container'
|
@ -0,0 +1,34 @@
|
||||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow } from 'enzyme'
|
||||
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display.component'
|
||||
import CurrencyDisplay from '../../currency-display'
|
||||
|
||||
describe('UserPreferencedCurrencyDisplay Component', () => {
|
||||
describe('rendering', () => {
|
||||
it('should render properly', () => {
|
||||
const wrapper = shallow(
|
||||
<UserPreferencedCurrencyDisplay />
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find(CurrencyDisplay).length, 1)
|
||||
})
|
||||
|
||||
it('should pass all props to the CurrencyDisplay child component', () => {
|
||||
const wrapper = shallow(
|
||||
<UserPreferencedCurrencyDisplay
|
||||
prop1={true}
|
||||
prop2="test"
|
||||
prop3={1}
|
||||
/>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find(CurrencyDisplay).length, 1)
|
||||
assert.equal(wrapper.find(CurrencyDisplay).props().prop1, true)
|
||||
assert.equal(wrapper.find(CurrencyDisplay).props().prop2, 'test')
|
||||
assert.equal(wrapper.find(CurrencyDisplay).props().prop3, 1)
|
||||
})
|
||||
})
|
||||
})
|
@ -0,0 +1,105 @@
|
||||
import assert from 'assert'
|
||||
import proxyquire from 'proxyquire'
|
||||
|
||||
let mapStateToProps, mergeProps
|
||||
|
||||
proxyquire('../user-preferenced-currency-display.container.js', {
|
||||
'react-redux': {
|
||||
connect: (ms, md, mp) => {
|
||||
mapStateToProps = ms
|
||||
mergeProps = mp
|
||||
return () => ({})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
describe('UserPreferencedCurrencyDisplay container', () => {
|
||||
describe('mapStateToProps()', () => {
|
||||
it('should return the correct props', () => {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
preferences: {
|
||||
useETHAsPrimaryCurrency: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
useETHAsPrimaryCurrency: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('mergeProps()', () => {
|
||||
it('should return the correct props', () => {
|
||||
const mockDispatchProps = {}
|
||||
|
||||
const tests = [
|
||||
{
|
||||
stateProps: {
|
||||
useETHAsPrimaryCurrency: true,
|
||||
},
|
||||
ownProps: {
|
||||
type: 'PRIMARY',
|
||||
},
|
||||
result: {
|
||||
currency: 'ETH',
|
||||
numberOfDecimals: 6,
|
||||
prefix: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
stateProps: {
|
||||
useETHAsPrimaryCurrency: false,
|
||||
},
|
||||
ownProps: {
|
||||
type: 'PRIMARY',
|
||||
},
|
||||
result: {
|
||||
currency: undefined,
|
||||
numberOfDecimals: 2,
|
||||
prefix: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
stateProps: {
|
||||
useETHAsPrimaryCurrency: true,
|
||||
},
|
||||
ownProps: {
|
||||
type: 'SECONDARY',
|
||||
fiatNumberOfDecimals: 4,
|
||||
fiatPrefix: '-',
|
||||
},
|
||||
result: {
|
||||
currency: undefined,
|
||||
numberOfDecimals: 4,
|
||||
prefix: '-',
|
||||
},
|
||||
},
|
||||
{
|
||||
stateProps: {
|
||||
useETHAsPrimaryCurrency: false,
|
||||
},
|
||||
ownProps: {
|
||||
type: 'SECONDARY',
|
||||
fiatNumberOfDecimals: 4,
|
||||
numberOfDecimals: 3,
|
||||
fiatPrefix: 'a',
|
||||
prefix: 'b',
|
||||
},
|
||||
result: {
|
||||
currency: 'ETH',
|
||||
numberOfDecimals: 3,
|
||||
prefix: 'b',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
tests.forEach(({ stateProps, ownProps, result }) => {
|
||||
assert.deepEqual(mergeProps({ ...stateProps }, mockDispatchProps, { ...ownProps }), {
|
||||
...result,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@ -0,0 +1,45 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { PRIMARY, SECONDARY, ETH } from '../../constants/common'
|
||||
import CurrencyDisplay from '../currency-display'
|
||||
|
||||
export default class UserPreferencedCurrencyDisplay extends PureComponent {
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
prefix: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
numberOfDecimals: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
hideLabel: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
showEthLogo: PropTypes.bool,
|
||||
ethLogoHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
// Used in container
|
||||
type: PropTypes.oneOf([PRIMARY, SECONDARY]),
|
||||
ethNumberOfDecimals: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
fiatNumberOfDecimals: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
ethPrefix: PropTypes.string,
|
||||
fiatPrefix: PropTypes.string,
|
||||
// From container
|
||||
currency: PropTypes.string,
|
||||
}
|
||||
|
||||
renderEthLogo () {
|
||||
const { currency, showEthLogo, ethLogoHeight = 12 } = this.props
|
||||
|
||||
return currency === ETH && showEthLogo && (
|
||||
<img
|
||||
src="/images/eth.svg"
|
||||
height={ethLogoHeight}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<CurrencyDisplay
|
||||
{...this.props}
|
||||
prefixComponent={this.renderEthLogo()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
import { connect } from 'react-redux'
|
||||
import UserPreferencedCurrencyDisplay from './user-preferenced-currency-display.component'
|
||||
import { preferencesSelector } from '../../selectors'
|
||||
import { ETH, PRIMARY, SECONDARY } from '../../constants/common'
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { useETHAsPrimaryCurrency } = preferencesSelector(state)
|
||||
|
||||
return {
|
||||
useETHAsPrimaryCurrency,
|
||||
}
|
||||
}
|
||||
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||
const { useETHAsPrimaryCurrency, ...restStateProps } = stateProps
|
||||
const {
|
||||
type,
|
||||
numberOfDecimals: propsNumberOfDecimals,
|
||||
ethNumberOfDecimals,
|
||||
fiatNumberOfDecimals,
|
||||
ethPrefix,
|
||||
fiatPrefix,
|
||||
prefix: propsPrefix,
|
||||
...restOwnProps
|
||||
} = ownProps
|
||||
|
||||
let currency, numberOfDecimals, prefix
|
||||
|
||||
if (type === PRIMARY && useETHAsPrimaryCurrency ||
|
||||
type === SECONDARY && !useETHAsPrimaryCurrency) {
|
||||
// Display ETH
|
||||
currency = ETH
|
||||
numberOfDecimals = propsNumberOfDecimals || ethNumberOfDecimals || 6
|
||||
prefix = propsPrefix || ethPrefix
|
||||
} else if (type === SECONDARY && useETHAsPrimaryCurrency ||
|
||||
type === PRIMARY && !useETHAsPrimaryCurrency) {
|
||||
// Display Fiat
|
||||
numberOfDecimals = propsNumberOfDecimals || fiatNumberOfDecimals || 2
|
||||
prefix = propsPrefix || fiatPrefix
|
||||
}
|
||||
|
||||
return {
|
||||
...restStateProps,
|
||||
...dispatchProps,
|
||||
...restOwnProps,
|
||||
currency,
|
||||
numberOfDecimals,
|
||||
prefix,
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null, mergeProps)(UserPreferencedCurrencyDisplay)
|
@ -0,0 +1 @@
|
||||
export { default } from './user-preferenced-currency-input.container'
|
@ -0,0 +1,32 @@
|
||||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow } from 'enzyme'
|
||||
import UserPreferencedCurrencyInput from '../user-preferenced-currency-input.component'
|
||||
import CurrencyInput from '../../currency-input'
|
||||
|
||||
describe('UserPreferencedCurrencyInput Component', () => {
|
||||
describe('rendering', () => {
|
||||
it('should render properly', () => {
|
||||
const wrapper = shallow(
|
||||
<UserPreferencedCurrencyInput />
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find(CurrencyInput).length, 1)
|
||||
})
|
||||
|
||||
it('should render useFiat for CurrencyInput based on preferences.useETHAsPrimaryCurrency', () => {
|
||||
const wrapper = shallow(
|
||||
<UserPreferencedCurrencyInput
|
||||
useETHAsPrimaryCurrency
|
||||
/>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find(CurrencyInput).length, 1)
|
||||
assert.equal(wrapper.find(CurrencyInput).props().useFiat, false)
|
||||
wrapper.setProps({ useETHAsPrimaryCurrency: false })
|
||||
assert.equal(wrapper.find(CurrencyInput).props().useFiat, true)
|
||||
})
|
||||
})
|
||||
})
|
@ -0,0 +1,31 @@
|
||||
import assert from 'assert'
|
||||
import proxyquire from 'proxyquire'
|
||||
|
||||
let mapStateToProps
|
||||
|
||||
proxyquire('../user-preferenced-currency-input.container.js', {
|
||||
'react-redux': {
|
||||
connect: ms => {
|
||||
mapStateToProps = ms
|
||||
return () => ({})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
describe('UserPreferencedCurrencyInput container', () => {
|
||||
describe('mapStateToProps()', () => {
|
||||
it('should return the correct props', () => {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
preferences: {
|
||||
useETHAsPrimaryCurrency: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
useETHAsPrimaryCurrency: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@ -0,0 +1,20 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import CurrencyInput from '../currency-input'
|
||||
|
||||
export default class UserPreferencedCurrencyInput extends PureComponent {
|
||||
static propTypes = {
|
||||
useETHAsPrimaryCurrency: PropTypes.bool,
|
||||
}
|
||||
|
||||
render () {
|
||||
const { useETHAsPrimaryCurrency, ...restProps } = this.props
|
||||
|
||||
return (
|
||||
<CurrencyInput
|
||||
{...restProps}
|
||||
useFiat={!useETHAsPrimaryCurrency}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import { connect } from 'react-redux'
|
||||
import UserPreferencedCurrencyInput from './user-preferenced-currency-input.component'
|
||||
import { preferencesSelector } from '../../selectors'
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { useETHAsPrimaryCurrency } = preferencesSelector(state)
|
||||
|
||||
return {
|
||||
useETHAsPrimaryCurrency,
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(UserPreferencedCurrencyInput)
|
1
ui/app/components/user-preferenced-token-input/index.js
Normal file
1
ui/app/components/user-preferenced-token-input/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './user-preferenced-token-input.container'
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user