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": { |
"metamask": { |
||||||
"isInitialized": true, |
"isInitialized": true, |
||||||
"isUnlocked": true, |
"isUnlocked": false, |
||||||
|
"isEthConfirmed": true, |
||||||
"currentDomain": "example.com", |
"currentDomain": "example.com", |
||||||
"rpcTarget": "https://rawtestrpc.metamask.io/", |
"rpcTarget": "https://rawtestrpc.metamask.io/", |
||||||
"identities": { |
"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 |
|
||||||
} |
|
||||||
}, |
|
||||||
"unconfTxs": {}, |
"unconfTxs": {}, |
||||||
"accounts": { |
"currentFiat": "USD", |
||||||
"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6": { |
"conversionRate": 11.4379398, |
||||||
"balance": "0x0", |
"conversionDate": 1473358355, |
||||||
"nonce": "0x0", |
"accounts": {}, |
||||||
"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" |
|
||||||
} |
|
||||||
}, |
|
||||||
"transactions": [], |
"transactions": [], |
||||||
"selectedAddress": "0x843963b837841dad3b0f5969ff271108776616df", |
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", |
||||||
"network": "2", |
"network": "1473186153102", |
||||||
|
"seedWords": null, |
||||||
"isConfirmed": true, |
"isConfirmed": true, |
||||||
"unconfMsgs": {}, |
"unconfMsgs": {}, |
||||||
"messages": [], |
"messages": [], |
||||||
|
"shapeShiftTxList": [], |
||||||
"provider": { |
"provider": { |
||||||
"type": "testnet" |
"type": "rpc", |
||||||
|
"rpcTarget": "http://localhost:8545" |
||||||
}, |
}, |
||||||
"selectedAccount": "0x843963b837841dad3b0f5969ff271108776616df" |
"selectedAccount": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" |
||||||
}, |
}, |
||||||
"appState": { |
"appState": { |
||||||
"menuOpen": false, |
"menuOpen": false, |
||||||
"currentView": { |
"currentView": { |
||||||
"name": "accountDetail" |
"name": "accountDetail", |
||||||
|
"detailView": null, |
||||||
|
"context": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" |
||||||
}, |
}, |
||||||
"accountDetail": { |
"accountDetail": { |
||||||
"subview": "transactions", |
"subview": "transactions" |
||||||
"accountExport": "none", |
|
||||||
"privateKey": "" |
|
||||||
}, |
}, |
||||||
"currentDomain": "testfaucet.metamask.io", |
"currentDomain": "127.0.0.1:9966", |
||||||
"transForward": false, |
"transForward": true, |
||||||
"isLoading": false, |
"isLoading": false, |
||||||
"warning": null, |
"warning": null |
||||||
"scrollToBottom": false |
|
||||||
}, |
}, |
||||||
"identities": {} |
"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