feature/default_network_editable
commit
850b6d1440
@ -0,0 +1,16 @@ |
||||
<!doctype html> |
||||
<html> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<title>MetaMask Notification</title> |
||||
<style> |
||||
body { |
||||
overflow: hidden; |
||||
} |
||||
</style> |
||||
</head> |
||||
<body> |
||||
<div id="app-content"></div> |
||||
<script src="./scripts/popup.js" type="text/javascript" charset="utf-8"></script> |
||||
</body> |
||||
</html> |
@ -0,0 +1,8 @@ |
||||
module.exports = function isPopupOrNotification() { |
||||
const url = window.location.href |
||||
if (url.match(/popup.html$/)) { |
||||
return 'popup' |
||||
} else { |
||||
return 'notification' |
||||
} |
||||
} |
File diff suppressed because one or more lines are too long
@ -1,84 +1,44 @@ |
||||
{ |
||||
"metamask": { |
||||
"isInitialized": true, |
||||
"isUnlocked": true, |
||||
"isUnlocked": false, |
||||
"isEthConfirmed": true, |
||||
"currentDomain": "example.com", |
||||
"rpcTarget": "https://rawtestrpc.metamask.io/", |
||||
"identities": { |
||||
"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6": { |
||||
"name": "Wallet 1", |
||||
"address": "0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6", |
||||
"mayBeFauceting": false |
||||
}, |
||||
"0x843963b837841dad3b0f5969ff271108776616df": { |
||||
"name": "Wallet 2", |
||||
"address": "0x843963b837841dad3b0f5969ff271108776616df", |
||||
"mayBeFauceting": false |
||||
}, |
||||
"0x2cb215323857bec1c91e5db10fe87379a5cf129a": { |
||||
"name": "Wallet 3", |
||||
"address": "0x2cb215323857bec1c91e5db10fe87379a5cf129a", |
||||
"mayBeFauceting": false |
||||
}, |
||||
"0xc5091450b7548b0dce3a76b8d325929c39e648d1": { |
||||
"name": "Wallet 4", |
||||
"address": "0xc5091450b7548b0dce3a76b8d325929c39e648d1", |
||||
"mayBeFauceting": false |
||||
} |
||||
}, |
||||
"identities": {}, |
||||
"unconfTxs": {}, |
||||
"accounts": { |
||||
"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6": { |
||||
"balance": "0x0", |
||||
"nonce": "0x0", |
||||
"code": "0x", |
||||
"address": "0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6" |
||||
}, |
||||
"0x843963b837841dad3b0f5969ff271108776616df": { |
||||
"balance": "0x0", |
||||
"nonce": "0x0", |
||||
"code": "0x", |
||||
"address": "0x843963b837841dad3b0f5969ff271108776616df" |
||||
}, |
||||
"0x2cb215323857bec1c91e5db10fe87379a5cf129a": { |
||||
"balance": "0x0", |
||||
"nonce": "0x0", |
||||
"code": "0x", |
||||
"address": "0x2cb215323857bec1c91e5db10fe87379a5cf129a" |
||||
}, |
||||
"0xc5091450b7548b0dce3a76b8d325929c39e648d1": { |
||||
"balance": "0x0", |
||||
"nonce": "0x0", |
||||
"code": "0x", |
||||
"address": "0xc5091450b7548b0dce3a76b8d325929c39e648d1" |
||||
} |
||||
}, |
||||
"currentFiat": "USD", |
||||
"conversionRate": 11.4379398, |
||||
"conversionDate": 1473358355, |
||||
"accounts": {}, |
||||
"transactions": [], |
||||
"selectedAddress": "0x843963b837841dad3b0f5969ff271108776616df", |
||||
"network": "2", |
||||
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", |
||||
"network": "1473186153102", |
||||
"seedWords": null, |
||||
"isConfirmed": true, |
||||
"unconfMsgs": {}, |
||||
"messages": [], |
||||
"shapeShiftTxList": [], |
||||
"provider": { |
||||
"type": "testnet" |
||||
"type": "rpc", |
||||
"rpcTarget": "http://localhost:8545" |
||||
}, |
||||
"selectedAccount": "0x843963b837841dad3b0f5969ff271108776616df" |
||||
"selectedAccount": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" |
||||
}, |
||||
"appState": { |
||||
"menuOpen": false, |
||||
"currentView": { |
||||
"name": "accountDetail" |
||||
"name": "accountDetail", |
||||
"detailView": null, |
||||
"context": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" |
||||
}, |
||||
"accountDetail": { |
||||
"subview": "transactions", |
||||
"accountExport": "none", |
||||
"privateKey": "" |
||||
"subview": "transactions" |
||||
}, |
||||
"currentDomain": "testfaucet.metamask.io", |
||||
"transForward": false, |
||||
"currentDomain": "127.0.0.1:9966", |
||||
"transForward": true, |
||||
"isLoading": false, |
||||
"warning": null, |
||||
"scrollToBottom": false |
||||
"warning": null |
||||
}, |
||||
"identities": {} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,39 @@ |
||||
{ |
||||
"metamask": { |
||||
"isInitialized": false, |
||||
"isUnlocked": false, |
||||
"isEthConfirmed": false, |
||||
"currentDomain": "example.com", |
||||
"rpcTarget": "https://rawtestrpc.metamask.io/", |
||||
"identities": {}, |
||||
"unconfTxs": {}, |
||||
"currentFiat": "USD", |
||||
"conversionRate": 0, |
||||
"conversionDate": "N/A", |
||||
"accounts": {}, |
||||
"transactions": [], |
||||
"seedWords": null, |
||||
"isConfirmed": true, |
||||
"unconfMsgs": {}, |
||||
"messages": [], |
||||
"shapeShiftTxList": [], |
||||
"provider": { |
||||
"type": "testnet" |
||||
}, |
||||
"network": "2" |
||||
}, |
||||
"appState": { |
||||
"menuOpen": false, |
||||
"currentView": { |
||||
"name": "restoreVault" |
||||
}, |
||||
"accountDetail": { |
||||
"subview": "transactions" |
||||
}, |
||||
"currentDomain": "extensions", |
||||
"transForward": true, |
||||
"isLoading": false, |
||||
"warning": null |
||||
}, |
||||
"identities": {} |
||||
} |
@ -0,0 +1,76 @@ |
||||
{ |
||||
"metamask": { |
||||
"isInitialized": true, |
||||
"isUnlocked": true, |
||||
"isEthConfirmed": false, |
||||
"currentDomain": "example.com", |
||||
"rpcTarget": "https://rawtestrpc.metamask.io/", |
||||
"identities": { |
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { |
||||
"name": "Wallet 1", |
||||
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", |
||||
"mayBeFauceting": false |
||||
}, |
||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { |
||||
"name": "Wallet 2", |
||||
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", |
||||
"mayBeFauceting": false |
||||
}, |
||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { |
||||
"name": "Wallet 3", |
||||
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d", |
||||
"mayBeFauceting": false |
||||
} |
||||
}, |
||||
"unconfTxs": {}, |
||||
"currentFiat": "USD", |
||||
"conversionRate": 11.21283484, |
||||
"conversionDate": 1472158984, |
||||
"accounts": { |
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { |
||||
"code": "0x", |
||||
"balance": "0x34693f54a1e25900", |
||||
"nonce": "0x100013", |
||||
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" |
||||
}, |
||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { |
||||
"code": "0x", |
||||
"nonce": "0x100000", |
||||
"balance": "0x18af912cee770000", |
||||
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb" |
||||
}, |
||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { |
||||
"code": "0x", |
||||
"nonce": "0x100000", |
||||
"balance": "0x2386f26fc10000", |
||||
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d" |
||||
} |
||||
}, |
||||
"transactions": [], |
||||
"network": "2", |
||||
"seedWords": null, |
||||
"isConfirmed": true, |
||||
"unconfMsgs": {}, |
||||
"messages": [], |
||||
"shapeShiftTxList": [], |
||||
"provider": { |
||||
"type": "testnet" |
||||
}, |
||||
"selectedAccount": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" |
||||
}, |
||||
"appState": { |
||||
"menuOpen": false, |
||||
"currentView": { |
||||
"name": "sendTransaction" |
||||
}, |
||||
"accountDetail": { |
||||
"subview": "transactions" |
||||
}, |
||||
"currentDomain": "127.0.0.1:9966", |
||||
"transForward": true, |
||||
"isLoading": false, |
||||
"warning": null, |
||||
"detailView": {} |
||||
}, |
||||
"identities": {} |
||||
} |
@ -0,0 +1,348 @@ |
||||
{ |
||||
"metamask": { |
||||
"isInitialized": true, |
||||
"isUnlocked": true, |
||||
"isEthConfirmed": true, |
||||
"currentDomain": "example.com", |
||||
"rpcTarget": "https://rawtestrpc.metamask.io/", |
||||
"identities": { |
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { |
||||
"name": "Wallet 1", |
||||
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", |
||||
"mayBeFauceting": false |
||||
}, |
||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { |
||||
"name": "Wallet 2", |
||||
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", |
||||
"mayBeFauceting": false |
||||
}, |
||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { |
||||
"name": "Wallet 3", |
||||
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d", |
||||
"mayBeFauceting": false |
||||
} |
||||
}, |
||||
"unconfTxs": {}, |
||||
"currentFiat": "USD", |
||||
"conversionRate": 11.21274318, |
||||
"conversionDate": 1472159644, |
||||
"accounts": { |
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { |
||||
"code": "0x", |
||||
"nonce": "0x13", |
||||
"balance": "0x461d4a64e937d3d1", |
||||
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" |
||||
}, |
||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { |
||||
"code": "0x", |
||||
"nonce": "0x0", |
||||
"balance": "0x0", |
||||
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb" |
||||
}, |
||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { |
||||
"code": "0x", |
||||
"balance": "0x0", |
||||
"nonce": "0x0", |
||||
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d" |
||||
} |
||||
}, |
||||
"transactions": [], |
||||
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", |
||||
"network": "1", |
||||
"seedWords": null, |
||||
"isConfirmed": true, |
||||
"unconfMsgs": {}, |
||||
"messages": [], |
||||
"shapeShiftTxList": [], |
||||
"provider": { |
||||
"type": "mainnet" |
||||
}, |
||||
"selectedAccount": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" |
||||
}, |
||||
"appState": { |
||||
"menuOpen": false, |
||||
"currentView": { |
||||
"name": "buyEth", |
||||
"context": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" |
||||
}, |
||||
"accountDetail": { |
||||
"subview": "transactions" |
||||
}, |
||||
"currentDomain": "127.0.0.1:9966", |
||||
"transForward": true, |
||||
"isLoading": false, |
||||
"detailView": {}, |
||||
"buyView": { |
||||
"subview": "buyForm", |
||||
"formView": { |
||||
"coinbase": false, |
||||
"shapeshift": true, |
||||
"marketinfo": { |
||||
"pair": "btc_eth", |
||||
"rate": 51.14252949, |
||||
"minerFee": 0.01, |
||||
"limit": 2.60306578, |
||||
"minimum": 0.00038935, |
||||
"maxLimit": 8.67688592 |
||||
}, |
||||
"coinOptions": { |
||||
"BTC": { |
||||
"name": "Bitcoin", |
||||
"symbol": "BTC", |
||||
"image": "https://shapeshift.io/images/coins/bitcoin.png", |
||||
"status": "available" |
||||
}, |
||||
"BCY": { |
||||
"name": "BitCrystals", |
||||
"symbol": "BCY", |
||||
"image": "https://shapeshift.io/images/coins/bitcrystals.png", |
||||
"status": "available" |
||||
}, |
||||
"BLK": { |
||||
"name": "Blackcoin", |
||||
"symbol": "BLK", |
||||
"image": "https://shapeshift.io/images/coins/blackcoin.png", |
||||
"status": "available" |
||||
}, |
||||
"BTS": { |
||||
"name": "Bitshares", |
||||
"symbol": "BTS", |
||||
"specialReturn": false, |
||||
"specialOutgoing": true, |
||||
"specialIncoming": true, |
||||
"fieldName": "destTag", |
||||
"fieldKey": "destTag", |
||||
"image": "https://shapeshift.io/images/coins/bitshares.png", |
||||
"status": "available" |
||||
}, |
||||
"CLAM": { |
||||
"name": "Clams", |
||||
"symbol": "CLAM", |
||||
"image": "https://shapeshift.io/images/coins/clams.png", |
||||
"status": "available" |
||||
}, |
||||
"DASH": { |
||||
"name": "Dash", |
||||
"symbol": "DASH", |
||||
"image": "https://shapeshift.io/images/coins/dash.png", |
||||
"status": "available" |
||||
}, |
||||
"DGB": { |
||||
"name": "Digibyte", |
||||
"symbol": "DGB", |
||||
"image": "https://shapeshift.io/images/coins/digibyte.png", |
||||
"status": "available" |
||||
}, |
||||
"DAO": { |
||||
"name": "TheDao", |
||||
"symbol": "DAO", |
||||
"image": "https://shapeshift.io/images/coins/thedao.png", |
||||
"status": "available" |
||||
}, |
||||
"DGD": { |
||||
"name": "DigixDao", |
||||
"symbol": "DGD", |
||||
"image": "https://shapeshift.io/images/coins/digixdao.png", |
||||
"status": "available" |
||||
}, |
||||
"DOGE": { |
||||
"name": "Dogecoin", |
||||
"symbol": "DOGE", |
||||
"image": "https://shapeshift.io/images/coins/dogecoin.png", |
||||
"status": "available" |
||||
}, |
||||
"EMC": { |
||||
"name": "Emercoin", |
||||
"symbol": "EMC", |
||||
"image": "https://shapeshift.io/images/coins/emercoin.png", |
||||
"status": "available" |
||||
}, |
||||
"ETH": { |
||||
"name": "Ether", |
||||
"symbol": "ETH", |
||||
"image": "https://shapeshift.io/images/coins/ether.png", |
||||
"status": "available" |
||||
}, |
||||
"ETC": { |
||||
"name": "Ether Classic", |
||||
"symbol": "ETC", |
||||
"image": "https://shapeshift.io/images/coins/etherclassic.png", |
||||
"status": "available" |
||||
}, |
||||
"FCT": { |
||||
"name": "Factoids", |
||||
"symbol": "FCT", |
||||
"image": "https://shapeshift.io/images/coins/factoids.png", |
||||
"status": "available" |
||||
}, |
||||
"LBC": { |
||||
"name": "LBRY Credits", |
||||
"symbol": "LBC", |
||||
"image": "https://shapeshift.io/images/coins/lbry.png", |
||||
"status": "available" |
||||
}, |
||||
"LSK": { |
||||
"name": "Lisk", |
||||
"symbol": "LSK", |
||||
"image": "https://shapeshift.io/images/coins/lisk.png", |
||||
"status": "available" |
||||
}, |
||||
"LTC": { |
||||
"name": "Litecoin", |
||||
"symbol": "LTC", |
||||
"image": "https://shapeshift.io/images/coins/litecoin.png", |
||||
"status": "available" |
||||
}, |
||||
"MAID": { |
||||
"name": "Maidsafe", |
||||
"symbol": "MAID", |
||||
"image": "https://shapeshift.io/images/coins/maidsafe.png", |
||||
"status": "available" |
||||
}, |
||||
"MINT": { |
||||
"name": "Mintcoin", |
||||
"symbol": "MINT", |
||||
"image": "https://shapeshift.io/images/coins/mintcoin.png", |
||||
"status": "available" |
||||
}, |
||||
"MONA": { |
||||
"name": "Monacoin", |
||||
"symbol": "MONA", |
||||
"image": "https://shapeshift.io/images/coins/monacoin.png", |
||||
"status": "available" |
||||
}, |
||||
"MSC": { |
||||
"name": "Omni", |
||||
"symbol": "MSC", |
||||
"image": "https://shapeshift.io/images/coins/mastercoin.png", |
||||
"status": "available" |
||||
}, |
||||
"NBT": { |
||||
"name": "Nubits", |
||||
"symbol": "NBT", |
||||
"image": "https://shapeshift.io/images/coins/nubits.png", |
||||
"status": "available" |
||||
}, |
||||
"NMC": { |
||||
"name": "Namecoin", |
||||
"symbol": "NMC", |
||||
"image": "https://shapeshift.io/images/coins/namecoin.png", |
||||
"status": "available" |
||||
}, |
||||
"NVC": { |
||||
"name": "Novacoin", |
||||
"symbol": "NVC", |
||||
"image": "https://shapeshift.io/images/coins/novacoin.png", |
||||
"status": "available" |
||||
}, |
||||
"NXT": { |
||||
"name": "Nxt", |
||||
"symbol": "NXT", |
||||
"specialReturn": false, |
||||
"specialOutgoing": true, |
||||
"specialIncoming": true, |
||||
"specialIncomingStatus": false, |
||||
"fieldName": "Public Key (only for unfunded accounts!)", |
||||
"fieldKey": "rsAddress", |
||||
"image": "https://shapeshift.io/images/coins/nxt.png", |
||||
"status": "available" |
||||
}, |
||||
"PPC": { |
||||
"name": "Peercoin", |
||||
"symbol": "PPC", |
||||
"image": "https://shapeshift.io/images/coins/peercoin.png", |
||||
"status": "available" |
||||
}, |
||||
"RDD": { |
||||
"name": "Reddcoin", |
||||
"symbol": "RDD", |
||||
"image": "https://shapeshift.io/images/coins/reddcoin.png", |
||||
"status": "available" |
||||
}, |
||||
"SDC": { |
||||
"name": "Shadowcash", |
||||
"symbol": "SDC", |
||||
"image": "https://shapeshift.io/images/coins/shadowcash.png", |
||||
"status": "available" |
||||
}, |
||||
"SC": { |
||||
"name": "Siacoin", |
||||
"symbol": "SC", |
||||
"image": "https://shapeshift.io/images/coins/siacoin.png", |
||||
"status": "available" |
||||
}, |
||||
"SJCX": { |
||||
"name": "StorjX", |
||||
"symbol": "SJCX", |
||||
"image": "https://shapeshift.io/images/coins/storjcoinx.png", |
||||
"status": "available" |
||||
}, |
||||
"START": { |
||||
"name": "Startcoin", |
||||
"symbol": "START", |
||||
"image": "https://shapeshift.io/images/coins/startcoin.png", |
||||
"status": "available" |
||||
}, |
||||
"STEEM": { |
||||
"name": "Steem", |
||||
"symbol": "STEEM", |
||||
"specialReturn": false, |
||||
"specialOutgoing": true, |
||||
"specialIncoming": true, |
||||
"fieldName": "destTag", |
||||
"fieldKey": "destTag", |
||||
"image": "https://shapeshift.io/images/coins/steem.png", |
||||
"status": "available" |
||||
}, |
||||
"USDT": { |
||||
"name": "Tether", |
||||
"symbol": "USDT", |
||||
"image": "https://shapeshift.io/images/coins/tether.png", |
||||
"status": "available" |
||||
}, |
||||
"VOX": { |
||||
"name": "Voxels", |
||||
"symbol": "VOX", |
||||
"image": "https://shapeshift.io/images/coins/voxels.png", |
||||
"status": "available" |
||||
}, |
||||
"VRC": { |
||||
"name": "Vericoin", |
||||
"symbol": "VRC", |
||||
"image": "https://shapeshift.io/images/coins/vericoin.png", |
||||
"status": "available" |
||||
}, |
||||
"VTC": { |
||||
"name": "Vertcoin", |
||||
"symbol": "VTC", |
||||
"image": "https://shapeshift.io/images/coins/vertcoin.png", |
||||
"status": "available" |
||||
}, |
||||
"XCP": { |
||||
"name": "Counterparty", |
||||
"symbol": "XCP", |
||||
"image": "https://shapeshift.io/images/coins/counterparty.png", |
||||
"status": "available" |
||||
}, |
||||
"XMR": { |
||||
"name": "Monero", |
||||
"symbol": "XMR", |
||||
"specialReturn": false, |
||||
"specialOutgoing": true, |
||||
"specialIncoming": true, |
||||
"fieldName": "Payment Id", |
||||
"qrName": "tx_payment_id", |
||||
"fieldKey": "paymentId", |
||||
"image": "https://shapeshift.io/images/coins/monero.png", |
||||
"status": "available" |
||||
} |
||||
} |
||||
}, |
||||
"buyAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", |
||||
"amount": "5.00", |
||||
"warning": null |
||||
}, |
||||
"isSubLoading": false |
||||
}, |
||||
"identities": {} |
||||
} |
@ -0,0 +1,28 @@ |
||||
# Form Persisting Architecture |
||||
|
||||
Since: |
||||
- The popup is torn down completely on every click outside of it. |
||||
- We have forms with multiple fields (like passwords & seed phrases) that might encourage a user to leave our panel to refer to a password manager. |
||||
|
||||
We cause user friction when we lose the contents of certain forms. |
||||
|
||||
This calls for an architecture of a form component that can completely persist its values to LocalStorage on every relevant change, and restore those values on reopening. |
||||
|
||||
To achieve this, we have defined a class, a subclass of `React.Component`, called `PersistentForm`, and it's stored at `ui/lib/persistent-form.js`. |
||||
|
||||
To use this class, simply take your form component (the component that renders `input`, `select`, or `textarea` elements), and make it subclass from `PersistentForm` instead of `React.Component`. |
||||
|
||||
You can see an example of this in use in `ui/app/first-time/restore-vault.js`. |
||||
|
||||
Additionally, any field whose value should be persisted, should have a `persistentFormId` attribute, which needs to be assigned under a `dataset` key on the main `attributes` hash. For example: |
||||
|
||||
```javascript |
||||
return h('textarea.twelve-word-phrase.letter-spacey', { |
||||
dataset: { |
||||
persistentFormId: 'wallet-seed', |
||||
}, |
||||
}) |
||||
``` |
||||
|
||||
That's it! This field should be persisted to `localStorage` on each `keyUp`, those values should be restored on view load, and the cached values should be cleared when navigating deliberately away from the form. |
||||
|
@ -0,0 +1,97 @@ |
||||
var assert = require('assert') |
||||
var MetaMaskController = require('../../app/scripts/metamask-controller') |
||||
var sinon = require('sinon') |
||||
var extend = require('xtend') |
||||
const STORAGE_KEY = 'metamask-config' |
||||
|
||||
describe('MetaMaskController', function() { |
||||
const noop = () => {} |
||||
let controller = new MetaMaskController({ |
||||
showUnconfirmedMessage: noop, |
||||
unlockAccountMessage: noop, |
||||
showUnconfirmedTx: noop, |
||||
setData, |
||||
loadData, |
||||
}) |
||||
|
||||
beforeEach(function() { |
||||
// sinon allows stubbing methods that are easily verified
|
||||
this.sinon = sinon.sandbox.create() |
||||
window.localStorage = {} // Hacking localStorage support into JSDom
|
||||
}) |
||||
|
||||
afterEach(function() { |
||||
// sinon requires cleanup otherwise it will overwrite context
|
||||
this.sinon.restore() |
||||
}) |
||||
|
||||
describe('#enforceTxValidations', function () { |
||||
it('returns null for positive values', function() { |
||||
var sample = { |
||||
value: '0x01' |
||||
} |
||||
var res = controller.enforceTxValidations(sample) |
||||
assert.equal(res, null, 'no error') |
||||
}) |
||||
|
||||
|
||||
it('returns error for negative values', function() { |
||||
var sample = { |
||||
value: '-0x01' |
||||
} |
||||
var res = controller.enforceTxValidations(sample) |
||||
assert.ok(res, 'error') |
||||
}) |
||||
}) |
||||
}) |
||||
|
||||
|
||||
function loadData () { |
||||
var oldData = getOldStyleData() |
||||
var newData |
||||
try { |
||||
newData = JSON.parse(window.localStorage[STORAGE_KEY]) |
||||
} catch (e) {} |
||||
|
||||
var data = extend({ |
||||
meta: { |
||||
version: 0, |
||||
}, |
||||
data: { |
||||
config: { |
||||
provider: { |
||||
type: 'testnet', |
||||
}, |
||||
}, |
||||
}, |
||||
}, oldData || null, newData || null) |
||||
return data |
||||
} |
||||
|
||||
function getOldStyleData () { |
||||
var config, wallet, seedWords |
||||
|
||||
var result = { |
||||
meta: { version: 0 }, |
||||
data: {}, |
||||
} |
||||
|
||||
try { |
||||
config = JSON.parse(window.localStorage['config']) |
||||
result.data.config = config |
||||
} catch (e) {} |
||||
try { |
||||
wallet = JSON.parse(window.localStorage['lightwallet']) |
||||
result.data.wallet = wallet |
||||
} catch (e) {} |
||||
try { |
||||
seedWords = window.localStorage['seedWords'] |
||||
result.data.seedWords = seedWords |
||||
} catch (e) {} |
||||
|
||||
return result |
||||
} |
||||
|
||||
function setData (data) { |
||||
window.localStorage[STORAGE_KEY] = JSON.stringify(data) |
||||
} |
@ -1,140 +0,0 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const formatBalance = require('../util').formatBalance |
||||
const generateBalanceObject = require('../util').generateBalanceObject |
||||
const Tooltip = require('./tooltip.js') |
||||
|
||||
module.exports = connect(mapStateToProps)(EthBalanceComponent) |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
conversionRate: state.metamask.conversionRate, |
||||
conversionDate: state.metamask.conversionDate, |
||||
currentFiat: state.metamask.currentFiat, |
||||
} |
||||
} |
||||
|
||||
inherits(EthBalanceComponent, Component) |
||||
function EthBalanceComponent () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
EthBalanceComponent.prototype.render = function () { |
||||
var state = this.props |
||||
var style = state.style |
||||
|
||||
const value = formatBalance(state.value, 6) |
||||
var width = state.width |
||||
|
||||
return ( |
||||
|
||||
h('.ether-balance', { |
||||
style: style, |
||||
}, [ |
||||
h('.ether-balance-amount', { |
||||
style: { |
||||
display: 'inline', |
||||
width: width, |
||||
}, |
||||
}, this.renderBalance(value, state)), |
||||
]) |
||||
|
||||
) |
||||
} |
||||
EthBalanceComponent.prototype.renderBalance = function (value, state) { |
||||
if (value === 'None') return value |
||||
var balanceObj = generateBalanceObject(value, state.shorten ? 1 : 3) |
||||
var balance, fiatDisplayNumber, fiatTooltipNumber |
||||
var splitBalance = value.split(' ') |
||||
var ethNumber = splitBalance[0] |
||||
var ethSuffix = splitBalance[1] |
||||
|
||||
|
||||
if (state.conversionRate !== 0) { |
||||
fiatTooltipNumber = Number(splitBalance[0]) * state.conversionRate |
||||
fiatDisplayNumber = fiatTooltipNumber.toFixed(2) |
||||
} else { |
||||
fiatDisplayNumber = 'N/A' |
||||
} |
||||
|
||||
var fiatSuffix = state.currentFiat |
||||
|
||||
if (state.shorten) { |
||||
balance = balanceObj.shortBalance |
||||
} else { |
||||
balance = balanceObj.balance |
||||
} |
||||
|
||||
var label = balanceObj.label |
||||
|
||||
return ( |
||||
h('.flex-column', [ |
||||
h(Tooltip, { |
||||
position: 'bottom', |
||||
title: `${ethNumber} ${ethSuffix}`, |
||||
}, [ |
||||
h('.flex-row', { |
||||
style: { |
||||
alignItems: 'flex-end', |
||||
lineHeight: '13px', |
||||
fontFamily: 'Montserrat Light', |
||||
textRendering: 'geometricPrecision', |
||||
marginBottom: '5px', |
||||
}, |
||||
}, [ |
||||
h('div', { |
||||
style: { |
||||
width: '100%', |
||||
textAlign: 'right', |
||||
}, |
||||
}, balance), |
||||
h('div', { |
||||
style: { |
||||
color: '#AEAEAE', |
||||
marginLeft: '5px', |
||||
}, |
||||
}, label), |
||||
]), |
||||
]), |
||||
h(Tooltip, { |
||||
position: 'bottom', |
||||
title: `${fiatTooltipNumber} ${fiatSuffix}`, |
||||
}, [ |
||||
fiatDisplay(fiatDisplayNumber, fiatSuffix), |
||||
]), |
||||
]) |
||||
) |
||||
} |
||||
|
||||
function fiatDisplay (fiatDisplayNumber, fiatSuffix) { |
||||
if (fiatDisplayNumber !== 'N/A') { |
||||
return h('.flex-row', { |
||||
style: { |
||||
alignItems: 'flex-end', |
||||
lineHeight: '13px', |
||||
fontFamily: 'Montserrat Light', |
||||
textRendering: 'geometricPrecision', |
||||
}, |
||||
}, [ |
||||
h('div', { |
||||
style: { |
||||
width: '100%', |
||||
textAlign: 'right', |
||||
fontSize: '12px', |
||||
color: '#333333', |
||||
}, |
||||
}, fiatDisplayNumber), |
||||
h('div', { |
||||
style: { |
||||
color: '#AEAEAE', |
||||
marginLeft: '5px', |
||||
fontSize: '12px', |
||||
}, |
||||
}, fiatSuffix), |
||||
]) |
||||
} else { |
||||
return h('div') |
||||
} |
||||
} |
@ -0,0 +1,71 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const formatBalance = require('../util').formatBalance |
||||
|
||||
module.exports = connect(mapStateToProps)(FiatValue) |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
conversionRate: state.metamask.conversionRate, |
||||
currentFiat: state.metamask.currentFiat, |
||||
} |
||||
} |
||||
|
||||
inherits(FiatValue, Component) |
||||
function FiatValue () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
FiatValue.prototype.render = function () { |
||||
const props = this.props |
||||
const value = formatBalance(props.value, 6) |
||||
|
||||
if (value === 'None') return value |
||||
var fiatDisplayNumber, fiatTooltipNumber |
||||
var splitBalance = value.split(' ') |
||||
|
||||
if (props.conversionRate !== 0) { |
||||
fiatTooltipNumber = Number(splitBalance[0]) * props.conversionRate |
||||
fiatDisplayNumber = fiatTooltipNumber.toFixed(2) |
||||
} else { |
||||
fiatDisplayNumber = 'N/A' |
||||
fiatTooltipNumber = 'Unknown' |
||||
} |
||||
|
||||
var fiatSuffix = props.currentFiat |
||||
|
||||
return fiatDisplay(fiatDisplayNumber, fiatSuffix) |
||||
} |
||||
|
||||
function fiatDisplay (fiatDisplayNumber, fiatSuffix) { |
||||
if (fiatDisplayNumber !== 'N/A') { |
||||
return h('.flex-row', { |
||||
style: { |
||||
alignItems: 'flex-end', |
||||
lineHeight: '13px', |
||||
fontFamily: 'Montserrat Light', |
||||
textRendering: 'geometricPrecision', |
||||
}, |
||||
}, [ |
||||
h('div', { |
||||
style: { |
||||
width: '100%', |
||||
textAlign: 'right', |
||||
fontSize: '12px', |
||||
color: '#333333', |
||||
}, |
||||
}, fiatDisplayNumber), |
||||
h('div', { |
||||
style: { |
||||
color: '#AEAEAE', |
||||
marginLeft: '5px', |
||||
fontSize: '12px', |
||||
}, |
||||
}, fiatSuffix), |
||||
]) |
||||
} else { |
||||
return h('div') |
||||
} |
||||
} |
@ -0,0 +1,61 @@ |
||||
const inherits = require('util').inherits |
||||
const Component = require('react').Component |
||||
const defaultKey = 'persistent-form-default' |
||||
const eventName = 'keyup' |
||||
|
||||
module.exports = PersistentForm |
||||
|
||||
function PersistentForm () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
inherits(PersistentForm, Component) |
||||
|
||||
PersistentForm.prototype.componentDidMount = function () { |
||||
const fields = document.querySelectorAll('[data-persistent-formid]') |
||||
const store = this.getPersistentStore() |
||||
|
||||
for (var i = 0; i < fields.length; i++) { |
||||
const field = fields[i] |
||||
const key = field.getAttribute('data-persistent-formid') |
||||
const cached = store[key] |
||||
if (cached !== undefined) { |
||||
field.value = cached |
||||
} |
||||
|
||||
field.addEventListener(eventName, this.persistentFieldDidUpdate.bind(this)) |
||||
} |
||||
} |
||||
|
||||
PersistentForm.prototype.getPersistentStore = function () { |
||||
let store = window.localStorage[this.persistentFormParentId || defaultKey] |
||||
if (store && store !== 'null') { |
||||
store = JSON.parse(store) |
||||
} else { |
||||
store = {} |
||||
} |
||||
return store |
||||
} |
||||
|
||||
PersistentForm.prototype.setPersistentStore = function (newStore) { |
||||
window.localStorage[this.persistentFormParentId || defaultKey] = JSON.stringify(newStore) |
||||
} |
||||
|
||||
PersistentForm.prototype.persistentFieldDidUpdate = function (event) { |
||||
const field = event.target |
||||
const store = this.getPersistentStore() |
||||
const key = field.getAttribute('data-persistent-formid') |
||||
const val = field.value |
||||
store[key] = val |
||||
this.setPersistentStore(store) |
||||
} |
||||
|
||||
PersistentForm.prototype.componentWillUnmount = function () { |
||||
const fields = document.querySelectorAll('[data-persistent-formid]') |
||||
for (var i = 0; i < fields.length; i++) { |
||||
const field = fields[i] |
||||
field.removeEventListener(eventName, this.persistentFieldDidUpdate.bind(this)) |
||||
} |
||||
this.setPersistentStore({}) |
||||
} |
||||
|
Loading…
Reference in new issue